From 01292b41740349747ed2748cb7aefa7123f6e386 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Tue, 11 Dec 2018 10:38:36 -0800 Subject: [PATCH 0001/1066] 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 0002/1066] 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 0003/1066] 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 df9bf3e59d6d0246ac337dd3068131b0bc86800f Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 13 Dec 2018 17:01:59 +0100 Subject: [PATCH 0004/1066] explorer use the new tree --- .../browser/parts/views/viewsViewlet.ts | 70 - src/vs/workbench/parts/files/common/files.ts | 2 +- .../files/electron-browser/explorerViewlet.ts | 19 +- .../files/electron-browser/fileActions.ts | 132 +- .../electron-browser/views/explorerView.ts | 476 +++-- .../electron-browser/views/explorerViewer.ts | 1535 ++++++++--------- 6 files changed, 981 insertions(+), 1253 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index cd256a1288f..767e16d82df 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -30,76 +30,6 @@ import { localize } from 'vs/nls'; import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views'; import { Registry } from 'vs/platform/registry/common/platform'; -export abstract class TreeViewsViewletPanel extends ViewletPanel { - - protected tree: WorkbenchTree; - - setExpanded(expanded: boolean): void { - if (this.isExpanded() !== expanded) { - this.updateTreeVisibility(this.tree, expanded); - super.setExpanded(expanded); - } - } - - setVisible(visible: boolean): void { - if (this.isVisible() !== visible) { - super.setVisible(visible); - this.updateTreeVisibility(this.tree, visible && this.isExpanded()); - } - } - - focus(): void { - super.focus(); - this.focusTree(); - } - - layoutBody(size: number): void { - if (this.tree) { - this.tree.layout(size); - } - } - - protected updateTreeVisibility(tree: WorkbenchTree, isVisible: boolean): void { - if (!tree) { - return; - } - - if (isVisible) { - DOM.show(tree.getHTMLElement()); - } else { - DOM.hide(tree.getHTMLElement()); // make sure the tree goes out of the tabindex world by hiding it - } - - if (isVisible) { - tree.onVisible(); - } else { - tree.onHidden(); - } - } - - private focusTree(): void { - if (!this.tree) { - return; // return early if viewlet has not yet been created - } - - // Make sure the current selected element is revealed - const selectedElement = this.tree.getSelection()[0]; - if (selectedElement) { - this.tree.reveal(selectedElement); - } - - // Pass Focus to Viewer - this.tree.domFocus(); - } - - dispose(): void { - if (this.tree) { - this.tree.dispose(); - } - super.dispose(); - } -} - export interface IViewletViewOptions extends IViewletPanelOptions { viewletState: object; } diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 36660df4223..03061cbd7e2 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -36,7 +36,7 @@ export interface IExplorerViewlet extends IViewlet { } export interface IExplorerView { - select(resource: URI, reveal?: boolean): TPromise; + select(resource: URI, reveal?: boolean): void; } /** diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index c7f73dc9eab..fb16a1dea62 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -5,13 +5,11 @@ import 'vs/css!./media/explorerviewlet'; import { localize } from 'vs/nls'; -import { IActionRunner } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition, IExplorerViewlet, VIEW_CONTAINER } from 'vs/workbench/parts/files/common/files'; import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { ActionRunner, FileViewletState } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; -import { ExplorerView, IExplorerViewOptions } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; +import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; import { EmptyView } from 'vs/workbench/parts/files/electron-browser/views/emptyView'; import { OpenEditorsView } from 'vs/workbench/parts/files/electron-browser/views/openEditorsView'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -150,7 +148,6 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi private static readonly EXPLORER_VIEWS_STATE = 'workbench.explorer.views.state'; - private fileViewletState: FileViewletState; private viewletVisibleContextKey: IContextKey; constructor( @@ -169,7 +166,6 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi ) { super(VIEWLET_ID, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); - this.fileViewletState = new FileViewletState(); this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService); this._register(this.contextService.onDidChangeWorkspaceName(e => this.updateTitleArea())); @@ -217,7 +213,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi }); const explorerInstantiator = this.instantiationService.createChild(new ServiceCollection([IEditorService, delegatingEditorService])); - return explorerInstantiator.createInstance(ExplorerView, { ...options, fileViewletState: this.fileViewletState }); + return explorerInstantiator.createInstance(ExplorerView, options); } return super.createView(viewDescriptor, options); } @@ -239,17 +235,6 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi super.setVisible(visible); } - public getActionRunner(): IActionRunner { - if (!this.actionRunner) { - this.actionRunner = new ActionRunner(this.fileViewletState); - } - return this.actionRunner; - } - - public getViewletState(): FileViewletState { - return this.fileViewletState; - } - focus(): void { const explorerView = this.getExplorerView(); if (explorerView && explorerView.isExpanded()) { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index a261d9c43dc..c9b164bd8a8 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -26,10 +26,9 @@ import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/file import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IInstantiationService, ServicesAccessor, IConstructorSignature2 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor, IConstructorSignature3 } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/parts/files/electron-browser/fileCommands'; @@ -49,6 +48,8 @@ import { Constants } from 'vs/editor/common/core/uint'; import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; +import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { EditableExplorerItems } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; export interface IEditableData { action: IAction; @@ -307,16 +308,14 @@ class RenameFileAction extends BaseRenameAction { /* Base New File/Folder Action */ export class BaseNewAction extends BaseFileAction { private presetFolder: ExplorerItem; - private tree: ITree; - private isFile: boolean; - private renameAction: BaseRenameAction; constructor( id: string, label: string, - tree: ITree, - isFile: boolean, - editableAction: BaseRenameAction, + private tree: AsyncDataTree, + private viewletState: IFileViewletState, + private isFile: boolean, + private renameAction: BaseRenameAction, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @@ -327,30 +326,19 @@ export class BaseNewAction extends BaseFileAction { if (element) { this.presetFolder = element.isDirectory ? element : element.parent; } - - this.tree = tree; - this.isFile = isFile; - this.renameAction = editableAction; } public run(context?: any): TPromise { - if (!context) { - return Promise.reject(new Error('No context provided to BaseNewAction.')); - } - - const viewletState = context.viewletState; - if (!viewletState) { - return Promise.reject(new Error('Invalid viewlet state provided to BaseNewAction.')); - } let folder = this.presetFolder; if (!folder) { - const focus = this.tree.getFocus(); + const focusedElements = this.tree.getFocus(); + const focus = focusedElements.length ? focusedElements[0] : undefined; + if (focus) { folder = focus.isDirectory ? focus : focus.parent; } else { - const input: ExplorerItem | Model = this.tree.getInput(); - folder = input instanceof Model ? input.roots[0] : input; + folder = this.tree.getNode(null).element; } } @@ -365,44 +353,31 @@ export class BaseNewAction extends BaseFileAction { return Promise.resolve(new Error('Parent folder is already in the process of creating a file')); } - return this.tree.reveal(folder, 0.5).then(() => { - return this.tree.expand(folder).then(() => { - const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); + this.tree.reveal(folder, 0.5); + return this.tree.expand(folder).then(() => { + const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); - this.renameAction.element = stat; + this.renameAction.element = stat; - viewletState.setEditable(stat, { - action: this.renameAction, - validator: (value) => { - const message = this.renameAction.validateFileName(folder, value); + this.viewletState.setEditable(stat, { + action: this.renameAction, + validator: (value) => { + const message = this.renameAction.validateFileName(folder, value); - if (!message) { - return null; - } - - return { - content: message, - formatContent: true, - type: MessageType.ERROR - }; + if (!message) { + return null; } - }); - return this.tree.refresh(folder).then(() => { - return this.tree.expand(folder).then(() => { - return this.tree.reveal(stat, 0.5).then(() => { - this.tree.setHighlight(stat); + return { + content: message, + formatContent: true, + type: MessageType.ERROR + }; + } + }); - const unbind = this.tree.onDidChangeHighlight((e: IHighlightEvent) => { - if (!e.highlight) { - stat.destroy(); - this.tree.refresh(folder); - unbind.dispose(); - } - }); - }); - }); - }); + return this.tree.refresh(folder).then(() => { + return this.tree.expand(folder).then(() => this.tree.reveal(stat, 0.5)); }); }); } @@ -412,14 +387,15 @@ export class BaseNewAction extends BaseFileAction { export class NewFileAction extends BaseNewAction { constructor( - tree: ITree, + tree: AsyncDataTree, + fileViewletState: EditableExplorerItems, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService ) { - super('explorer.newFile', NEW_FILE_LABEL, tree, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, notificationService, textFileService); + super('explorer.newFile', NEW_FILE_LABEL, tree, fileViewletState, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, notificationService, textFileService); this.class = 'explorer-action new-file'; this._updateEnablement(); @@ -430,14 +406,15 @@ export class NewFileAction extends BaseNewAction { export class NewFolderAction extends BaseNewAction { constructor( - tree: ITree, + tree: AsyncDataTree, + fileViewletState: EditableExplorerItems, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService ) { - super('explorer.newFolder', NEW_FOLDER_LABEL, tree, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, notificationService, textFileService); + super('explorer.newFolder', NEW_FOLDER_LABEL, tree, fileViewletState, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, notificationService, textFileService); this.class = 'explorer-action new-folder'; this._updateEnablement(); @@ -1315,7 +1292,7 @@ export class FocusFilesExplorer extends Action { const view = viewlet.getExplorerView(); if (view) { view.setExpanded(true); - view.getViewer().domFocus(); + view.focus(); } }); } @@ -1365,12 +1342,7 @@ export class CollapseExplorerView extends Action { return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { const explorerView = viewlet.getExplorerView(); if (explorerView) { - const viewer = explorerView.getViewer(); - if (viewer) { - const action = new CollapseAction(viewer, true, null); - action.run(); - action.dispose(); - } + explorerView.collapseAll(); } }); } @@ -1554,24 +1526,24 @@ class ClipboardContentProvider implements ITextModelContentProvider { } interface IExplorerContext { - viewletState: IFileViewletState; stat: ExplorerItem; selection: ExplorerItem[]; } -function getContext(listWidget: ListWidget, viewletService: IViewletService): IExplorerContext { +function getContext(listWidget: ListWidget): IExplorerContext { // These commands can only be triggered when explorer viewlet is visible so get it using the active viewlet - const tree = listWidget; - const stat = tree.getFocus(); + const tree = >listWidget; + const focus = tree.getFocus(); + const stat = focus.length ? focus[0] : undefined; const selection = tree.getSelection(); // Only respect the selection if user clicked inside it (focus belongs to it) - return { stat, selection: selection && selection.indexOf(stat) >= 0 ? selection : [], viewletState: (viewletService.getActiveViewlet()).getViewletState() }; + return { stat, selection: selection && selection.indexOf(stat) >= 0 ? selection : [] }; } // TODO@isidor these commands are calling into actions due to the complex inheritance action structure. // It should be the other way around, that actions call into commands. -function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature2): TPromise { +function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature3, EditableExplorerItems, ExplorerItem, Action>): TPromise { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); const viewletService = accessor.get(IViewletService); @@ -1585,10 +1557,10 @@ function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: ICons const explorerView = explorer.getExplorerView(); if (explorerView && explorerView.isVisible() && explorerView.isExpanded()) { explorerView.focus(); - const explorerContext = getContext(listService.lastFocusedList, viewletService); - const action = instantationService.createInstance(constructor, listService.lastFocusedList, explorerContext.stat); + const { stat } = getContext(listService.lastFocusedList); + const action = instantationService.createInstance(constructor, listService.lastFocusedList, explorerView.editableExplorerItems, stat); - return action.run(explorerContext); + return action.run(); } return undefined; @@ -1612,7 +1584,7 @@ CommandsRegistry.registerCommand({ export const renameHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); - const explorerContext = getContext(listService.lastFocusedList, accessor.get(IViewletService)); + const explorerContext = getContext(listService.lastFocusedList); const renameAction = instantationService.createInstance(TriggerRenameFileAction, listService.lastFocusedList, explorerContext.stat); return renameAction.run(explorerContext); @@ -1621,7 +1593,7 @@ export const renameHandler = (accessor: ServicesAccessor) => { export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); - const explorerContext = getContext(listService.lastFocusedList, accessor.get(IViewletService)); + const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; const moveFileToTrashAction = instantationService.createInstance(BaseDeleteFileAction, listService.lastFocusedList, stats, true); @@ -1631,7 +1603,7 @@ export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { export const deleteFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); - const explorerContext = getContext(listService.lastFocusedList, accessor.get(IViewletService)); + const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; const deleteFileAction = instantationService.createInstance(BaseDeleteFileAction, listService.lastFocusedList, stats, false); @@ -1641,7 +1613,7 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => { export const copyFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); - const explorerContext = getContext(listService.lastFocusedList, accessor.get(IViewletService)); + const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; const copyFileAction = instantationService.createInstance(CopyFileAction, listService.lastFocusedList, stats); @@ -1652,7 +1624,7 @@ export const pasteFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); const clipboardService = accessor.get(IClipboardService); - const explorerContext = getContext(listService.lastFocusedList, accessor.get(IViewletService)); + const explorerContext = getContext(listService.lastFocusedList); return Promise.all(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => { const pasteFileAction = instantationService.createInstance(PasteFileAction, listService.lastFocusedList, explorerContext.stat); 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 c3fbbc65e91..d4faa531a5d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { ThrottledDelayer, Delayer } from 'vs/base/common/async'; +import { ThrottledDelayer } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import * as glob from 'vs/base/common/glob'; @@ -15,14 +15,13 @@ import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext } from 'vs/workbench/parts/files/common/files'; import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService, FILES_EXCLUDE_CONFIG, IFileStat } from 'vs/platform/files/common/files'; -import { RefreshViewExplorerAction, NewFolderAction, NewFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { FileDragAndDrop, FileFilter, FileSorter, FileController, FileRenderer, FileDataSource, FileViewletState, FileAccessibilityProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { RefreshViewExplorerAction, NewFolderAction, NewFileAction, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { toResource } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as DOM from 'vs/base/browser/dom'; -import { CollapseAction } from 'vs/workbench/browser/viewlet'; -import { TreeViewsViewletPanel, FileIconThemableWorkbenchTree, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { ExplorerItem, Model } from 'vs/workbench/parts/files/common/explorerModel'; +import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ExplorerDecorationsProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -36,32 +35,37 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { isLinux } from 'vs/base/common/platform'; import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; -import { WorkbenchTree } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; +import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, EditableExplorerItems as EditableExplorerItems, FilesFilter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; +import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; export interface IExplorerViewOptions extends IViewletViewOptions { - fileViewletState: FileViewletState; + fileViewletState: EditableExplorerItems; } -export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView { +export class ExplorerView extends ViewletPanel implements IExplorerView { public static readonly ID: string = 'workbench.explorer.fileView'; private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes - private static readonly MEMENTO_LAST_ACTIVE_FILE_RESOURCE = 'explorer.memento.lastActiveFileResource'; - private static readonly MEMENTO_EXPANDED_FOLDER_RESOURCES = 'explorer.memento.expandedFolderResources'; - public readonly id: string = ExplorerView.ID; - private explorerViewer: WorkbenchTree; - private filter: FileFilter; - private fileViewletState: FileViewletState; + private tree: WorkbenchAsyncDataTree; + private filter: FilesFilter; + private isCreated: boolean; + private _editableExplorerItems: EditableExplorerItems; private explorerRefreshDelayer: ThrottledDelayer; @@ -75,8 +79,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private shouldRefresh: boolean; private autoReveal: boolean; private sortOrder: SortOrder; - private viewState: object; - private treeContainer: HTMLElement; private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private isDisposed: boolean; @@ -92,15 +94,18 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView @IFileService private fileService: IFileService, @IPartService private partService: IPartService, @IKeybindingService keybindingService: IKeybindingService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private contextKeyService: IContextKeyService, @IConfigurationService configurationService: IConfigurationService, @IDecorationsService decorationService: IDecorationsService, - @ILabelService private labelService: ILabelService + @ILabelService private labelService: ILabelService, + @IThemeService private themeService: IWorkbenchThemeService, + @IListService private listService: IListService, + @IMenuService private menuService: IMenuService, + @IClipboardService private clipboardService: IClipboardService ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); - this.viewState = options.viewletState; - this.fileViewletState = options.fileViewletState; + this._editableExplorerItems = new EditableExplorerItems(); this.autoReveal = true; this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); @@ -149,24 +154,27 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView setHeader(); } - public get name(): string { + protected layoutBody(size: number): void { + this.tree.layout(size); + } + + get name(): string { return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); } - public get title(): string { + get title(): string { return this.name; } - public set title(value: string) { + set title(value: string) { // noop } - public set name(value) { - // noop + get editableExplorerItems(): EditableExplorerItems { + return this._editableExplorerItems; } - public render(): void { - + render(): void { super.render(); // Update configuration @@ -174,11 +182,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView this.onConfigurationUpdated(configuration); // Load and Fill Viewer - let targetsToExpand: URI[] = []; - if (this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]) { - targetsToExpand = this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES].map((e: string) => URI.parse(e)); - } - this.doRefresh(targetsToExpand).then(() => { + this.doRefresh().then(() => { // When the explorer viewer is loaded, listen to changes to the editor input this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.revealActiveFile())); @@ -190,16 +194,16 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView }); } - public renderBody(container: HTMLElement): void { - this.treeContainer = DOM.append(container, DOM.$('.explorer-folders-view')); - this.tree = this.createViewer(this.treeContainer); + renderBody(container: HTMLElement): void { + const treeContainer = DOM.append(container, DOM.$('.explorer-folders-view')); + this.createTree(treeContainer); if (this.toolbar) { this.toolbar.setActions(this.getActions(), this.getSecondaryActions())(); } this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added))); - this.disposables.push(this.contextService.onDidChangeWorkbenchState(e => this.refreshFromEvent())); + this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.refreshFromEvent())); this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this.refreshFromEvent())); this.disposables.push(this.labelService.onDidRegisterFormatter(() => { this._onDidChangeTitleArea.fire(); @@ -207,20 +211,13 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView })); } - layoutBody(size: number): void { - if (this.treeContainer) { - this.treeContainer.style.height = size + 'px'; - } - super.layoutBody(size); - } - - public getActions(): IAction[] { + getActions(): IAction[] { const actions: Action[] = []; - actions.push(this.instantiationService.createInstance(NewFileAction, this.getViewer(), null)); - actions.push(this.instantiationService.createInstance(NewFolderAction, this.getViewer(), null)); + actions.push(this.instantiationService.createInstance(NewFileAction, this.tree, this._editableExplorerItems, null)); + actions.push(this.instantiationService.createInstance(NewFolderAction, this.tree, this._editableExplorerItems, null)); actions.push(this.instantiationService.createInstance(RefreshViewExplorerAction, this, 'explorer-action refresh-explorer')); - actions.push(this.instantiationService.createInstance(CollapseAction, this.getViewer(), true, 'explorer-action collapse-explorer')); + actions.push(this.instantiationService.createInstance(CollapseAction2, this.tree, true, 'explorer-action collapse-explorer')); return actions; } @@ -237,9 +234,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView const activeFile = this.getActiveFile(); if (activeFile) { - // Always remember last opened file - this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = activeFile.toString(); - // Select file if input is inside workspace if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { const selection = this.hasSingleSelection(activeFile); @@ -254,17 +248,16 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // Handle closed or untitled file (convince explorer to not reopen any file when getting visible) const activeInput = this.editorService.activeEditor; if (!activeInput || toResource(activeInput, { supportSideBySide: true, filter: Schemas.untitled })) { - this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = void 0; clearFocus = true; } // Otherwise clear if (clearSelection) { - this.explorerViewer.clearSelection(); + this.tree.setSelection([]); } if (clearFocus) { - this.explorerViewer.clearFocus(); + this.tree.setFocus([]); } } @@ -298,22 +291,22 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } } - public focus(): void { + focus(): void { super.focus(); let keepFocus = false; // Make sure the current selected element is revealed - if (this.explorerViewer) { + if (this.tree) { if (this.autoReveal) { - const selection = this.explorerViewer.getSelection(); + const selection = this.tree.getSelection(); if (selection.length > 0) { this.reveal(selection[0], 0.5); } } // Pass Focus to Viewer - this.explorerViewer.domFocus(); + this.tree.domFocus(); keepFocus = true; } @@ -324,12 +317,12 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } } - public setVisible(visible: boolean): void { + setVisible(visible: boolean): void { super.setVisible(visible); // Show if (visible) { - + DOM.show(this.tree.getHTMLElement()); // If a refresh was requested and we are now visible, run it let refreshPromise: Thenable = Promise.resolve(null); if (this.shouldRefresh) { @@ -357,13 +350,9 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } // Otherwise restore last used file: By lastActiveFileResource - let lastActiveFileResource: URI; - if (this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { - lastActiveFileResource = URI.parse(this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]); - } - - if (lastActiveFileResource && this.isCreated && this.model.findClosest(lastActiveFileResource)) { - this.editorService.openEditor({ resource: lastActiveFileResource, options: { revealIfVisible: true } }); + const focusedElements = this.tree.getFocus(); + if (focusedElements && focusedElements.length) { + this.editorService.openEditor({ resource: focusedElements[0].resource, options: { revealIfVisible: true } }); return; } @@ -371,11 +360,19 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView refreshPromise.then(() => { this.openFocusedElement(); }); + } else { + // make sure the tree goes out of the tabindex world by hiding it + DOM.hide(this.tree.getHTMLElement()); } } + collapseAll(): void { + this.tree.collapseAll(); + } + private openFocusedElement(preserveFocus?: boolean): void { - const stat: ExplorerItem = this.explorerViewer.getFocus(); + const focusedElements = this.tree.getFocus(); + const stat = focusedElements && focusedElements.length ? focusedElements[0] : undefined; if (stat && !stat.isDirectory) { this.editorService.openEditor({ resource: stat.resource, options: { preserveFocus, revealIfVisible: true } }); } @@ -393,10 +390,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView return toResource(input, { supportSideBySide: true }); } - private get isCreated(): boolean { - return !!(this.explorerViewer && this.explorerViewer.getInput()); - } - @memoize private get model(): Model { const model = this.instantiationService.createInstance(Model); @@ -405,73 +398,69 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView return model; } - private createViewer(container: HTMLElement): WorkbenchTree { - const dataSource = this.instantiationService.createInstance(FileDataSource); - const renderer = this.instantiationService.createInstance(FileRenderer, this.fileViewletState); - const controller = this.instantiationService.createInstance(FileController); - this.disposables.push(controller); - const sorter = this.instantiationService.createInstance(FileSorter); - this.disposables.push(sorter); - this.filter = this.instantiationService.createInstance(FileFilter); + private createTree(container: HTMLElement): void { + // TODO@isidor missing features + // const controller = this.instantiationService.createInstance(FileController); + // this.disposables.push(controller); + // const sorter = this.instantiationService.createInstance(FileSorter); + // this.disposables.push(sorter); + // const dnd = this.instantiationService.createInstance(FileDragAndDrop); + this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); - const dnd = this.instantiationService.createInstance(FileDragAndDrop); - const accessibilityProvider = this.instantiationService.createInstance(FileAccessibilityProvider); + const filesRenderer = this.instantiationService.createInstance(FilesRenderer, this._editableExplorerItems); + this.disposables.push(filesRenderer); - this.explorerViewer = this.instantiationService.createInstance(FileIconThemableWorkbenchTree, container, { - dataSource, - renderer, - controller, - sorter, - filter: this.filter, - dnd, - accessibilityProvider - }, { - autoExpandSingleChildren: true, - ariaLabel: nls.localize('treeAriaLabel', "Files Explorer") - }); + this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer], + this.instantiationService.createInstance(ExplorerDataSource, this.model), { + accessibilityProvider: new ExplorerAccessibilityProvider(), + ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), + identityProvider: { + getId: stat => stat.resource + }, + typeLabelProvider: { + getTypeLabel: stat => stat.name + }, + filter: this.filter + }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + filesRenderer.acquireTree(this.tree); // Bind context keys - FilesExplorerFocusedContext.bindTo(this.explorerViewer.contextKeyService); - ExplorerFocusedContext.bindTo(this.explorerViewer.contextKeyService); + FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); + ExplorerFocusedContext.bindTo(this.tree.contextKeyService); // Update Viewer based on File Change Events this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); // Update resource context based on focused element - this.disposables.push(this.explorerViewer.onDidChangeFocus((e: { focus: ExplorerItem }) => { + this.disposables.push(this.tree.onDidChangeFocus(e => { + const stat = e.elements && e.elements.length ? e.elements[0] : undefined; const isSingleFolder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER; - const resource = e.focus ? e.focus.resource : isSingleFolder ? this.contextService.getWorkspace().folders[0].uri : undefined; + const resource = stat ? stat.resource : isSingleFolder ? this.contextService.getWorkspace().folders[0].uri : undefined; this.resourceContext.set(resource); - this.folderContext.set((isSingleFolder && !e.focus) || e.focus && e.focus.isDirectory); - this.readonlyContext.set(e.focus && e.focus.isReadonly); - this.rootContext.set(!e.focus || (e.focus && e.focus.isRoot)); + this.folderContext.set((isSingleFolder && !stat) || stat && stat.isDirectory); + this.readonlyContext.set(stat && stat.isReadonly); + this.rootContext.set(!stat || (stat && stat.isRoot)); })); // Open when selecting via keyboard - this.disposables.push(this.explorerViewer.onDidChangeSelection(event => { - if (event && event.payload && event.payload.origin === 'keyboard') { - const element = this.tree.getSelection(); + this.disposables.push(this.tree.onDidChangeSelection(e => { + const selection = e.elements; + if (selection && selection.length) { + // TODO@Isidor check this, and add side by side, focus passing + const stat = selection[0]; - if (Array.isArray(element) && element[0] instanceof ExplorerItem) { - if (element[0].isDirectory) { - this.explorerViewer.toggleExpansion(element[0]); - } - - controller.openEditor(element[0], { pinned: false, sideBySide: false, preserveFocus: false }); + if (!stat.isDirectory) { + this.editorService.openEditor({ resource: stat.resource }); } } })); - return this.explorerViewer; + this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); } - getViewer(): WorkbenchTree { - return this.tree; - } - - public getOptimalWidth(): number { - const parentNode = this.explorerViewer.getHTMLElement(); + getOptimalWidth(): number { + const parentNode = this.tree.getHTMLElement(); const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels return DOM.getLargestChildWidth(parentNode, childNodes); @@ -501,15 +490,14 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } const childElement = ExplorerItem.create(addedElement, p.root); - p.removeChild(childElement); // make sure to remove any previous version of the file if any + // Make sure to remove any previous version of the file if any + p.removeChild(childElement); p.addChild(childElement); // Refresh the Parent (View) - this.explorerViewer.refresh(p).then(() => { - return this.reveal(childElement, 0.5).then(() => { - - // Focus new element - this.explorerViewer.setFocus(childElement); - }); + this.tree.refresh(p).then(() => { + // Reveal and focus new element + this.reveal(childElement, 0.5); + this.tree.setFocus([childElement]); }); }); }); @@ -526,7 +514,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView // Only update focus if renamed/moved element is selected let restoreFocus = false; - const focus: ExplorerItem = this.explorerViewer.getFocus(); + const focusedElements = this.tree.getFocus(); + const focus = focusedElements && focusedElements.length ? focusedElements[0] : undefined; if (focus && focus.resource && focus.resource.toString() === oldResource.toString()) { restoreFocus = true; } @@ -537,20 +526,20 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView const modelElements = this.model.findAll(oldResource); modelElements.forEach(modelElement => { //Check if element is expanded - isExpanded = this.explorerViewer.isExpanded(modelElement); + isExpanded = this.tree.isExpanded(modelElement); // Rename File (Model) modelElement.rename(newElement); // Update Parent (View) - this.explorerViewer.refresh(modelElement.parent).then(() => { + this.tree.refresh(modelElement.parent).then(() => { // Select in Viewer if set if (restoreFocus) { - this.explorerViewer.setFocus(modelElement); + this.tree.setFocus([modelElement]); } //Expand the element again if (isExpanded) { - this.explorerViewer.expand(modelElement); + this.tree.expand(modelElement); } }); }); @@ -568,10 +557,10 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView const oldParent = modelElement.parent; modelElement.move(newParents[index], (callback: () => void) => { // Update old parent - this.explorerViewer.refresh(oldParent).then(callback); + this.tree.refresh(oldParent).then(callback); }, () => { // Update new parent - this.explorerViewer.refresh(newParents[index], true).then(() => this.explorerViewer.expand(newParents[index])); + this.tree.refresh(newParents[index], true).then(() => this.tree.expand(newParents[index])); }); }); } @@ -588,12 +577,12 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView parent.removeChild(element); // Refresh Parent (View) - const restoreFocus = this.explorerViewer.isDOMFocused(); - this.explorerViewer.refresh(parent).then(() => { + const restoreFocus = document.activeElement === this.tree.getHTMLElement(); + this.tree.refresh(parent).then(() => { // Ensure viewer has keyboard focus if event originates from viewer if (restoreFocus) { - this.explorerViewer.domFocus(); + this.tree.domFocus(); } }); } @@ -602,17 +591,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } private onFileChanges(e: FileChangesEvent): void { - - // Ensure memento state does not capture a deleted file (we run this from a timeout because - // delete events can result in UI activity that will fill the memento again when multiple - // editors are closing) - setTimeout(() => { - const lastActiveResource: string = this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]; - if (lastActiveResource && e.contains(URI.parse(lastActiveResource), FileChangeType.DELETED)) { - this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE] = null; - } - }); - // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might // be fired first over the other or not at all. @@ -713,17 +691,13 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void { if (this.isVisible() && !this.isDisposed) { this.explorerRefreshDelayer.trigger(() => { - if (!this.explorerViewer.getHighlight()) { - return this.doRefresh(newRoots.map(r => r.uri)).then(() => { - if (newRoots.length === 1) { - return this.reveal(this.model.findClosest(newRoots[0].uri), 0.5); - } + return this.doRefresh().then(() => { + if (newRoots.length === 1) { + return this.reveal(this.model.findClosest(newRoots[0].uri), 0.5); + } - return undefined; - }); - } - - return Promise.resolve(null); + return undefined; + }); }); } else { this.shouldRefresh = true; @@ -733,22 +707,22 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView /** * Refresh the contents of the explorer to get up to date data from the disk about the file structure. */ - public refresh(): TPromise { - if (!this.explorerViewer || this.explorerViewer.getHighlight()) { + refresh(): TPromise { + if (!this.tree) { return Promise.resolve(null); } // Focus - this.explorerViewer.domFocus(); + this.tree.domFocus(); // Find resource to focus from active editor input if set let resourceToFocus: URI; if (this.autoReveal) { resourceToFocus = this.getActiveFile(); if (!resourceToFocus) { - const selection = this.explorerViewer.getSelection(); + const selection = this.tree.getSelection(); if (selection && selection.length === 1) { - resourceToFocus = (selection[0]).resource; + resourceToFocus = selection[0].resource; } } } @@ -762,7 +736,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView }); } - private doRefresh(targetsToExpand: URI[] = []): TPromise { + private doRefresh(): TPromise { const targetsToResolve = this.model.roots.map(root => ({ root, resource: root.resource, options: { resolveTo: [] } })); // First time refresh: Receive target through active editor input or selection and also include settings from previous session @@ -775,14 +749,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView found.options.resolveTo.push(activeFile); } } - - targetsToExpand.forEach(toExpand => { - const workspaceFolder = this.contextService.getWorkspaceFolder(toExpand); - if (workspaceFolder) { - const found = targetsToResolve.filter(ttr => ttr.resource.toString() === workspaceFolder.uri.toString()).pop(); - found.options.resolveTo.push(toExpand); - } - }); } // Subsequent refresh: Receive targets through expanded folders in tree @@ -792,7 +758,8 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView }); } - const promise = this.resolveRoots(targetsToResolve, targetsToExpand).then(result => { + const promise = this.resolveRoots(targetsToResolve).then(result => { + this.isCreated = true; this.decorationProvider.changed(targetsToResolve.map(t => t.root.resource)); return result; }); @@ -801,19 +768,13 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView return promise; } - private resolveRoots(targetsToResolve: { root: ExplorerItem, resource: URI, options: { resolveTo: any[] } }[], targetsToExpand: URI[]): TPromise { + private resolveRoots(targetsToResolve: { root: ExplorerItem, resource: URI, options: { resolveTo: any[] } }[]): TPromise { - // Display roots only when multi folder workspace - let input = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? this.model.roots[0] : this.model; - if (input !== this.explorerViewer.getInput()) { + if (!this.isCreated) { perf.mark('willResolveExplorer'); } const errorRoot = (resource: URI, root: ExplorerItem) => { - if (input === this.model.roots[0]) { - input = this.model; - } - return ExplorerItem.create({ resource: resource, name: paths.basename(resource.fsPath), @@ -823,17 +784,6 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView }, root, undefined, true); }; - const setInputAndExpand = (input: ExplorerItem | Model, statsToExpand: ExplorerItem[]) => { - // Make sure to expand all folders that where expanded in the previous session - // Special case: we are switching to multi workspace view, thus expand all the roots (they might just be added) - if (input === this.model && statsToExpand.every(fs => fs && !fs.isRoot)) { - statsToExpand = this.model.roots.concat(statsToExpand); - } - - return this.explorerViewer.setInput(input).then(() => this.explorerViewer.expandAll(statsToExpand)) - .then(() => perf.mark('didResolveExplorer')); - }; - if (targetsToResolve.every(t => t.root.resource.scheme === 'file')) { // All the roots are local, resolve them in parallel return this.fileService.resolveFiles(targetsToResolve).then(results => { @@ -852,19 +802,11 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView } }); - const statsToExpand: ExplorerItem[] = this.explorerViewer.getExpandedElements().concat(targetsToExpand.map(expand => this.model.findClosest(expand))); - if (input === this.explorerViewer.getInput()) { - return this.explorerViewer.refresh().then(() => this.explorerViewer.expandAll(statsToExpand)); - } - - return setInputAndExpand(input, statsToExpand); + return this.tree.refresh(null); }); } // There is a remote root, resolve the roots sequantally - let statsToExpand: ExplorerItem[] = []; - let delayer = new Delayer(100); - let delayerPromise: TPromise; return Promise.all(targetsToResolve.map((target, index) => this.fileService.resolveFile(target.resource, target.options) .then(result => result.isDirectory ? ExplorerItem.create(result, target.root, target.options.resolveTo) : errorRoot(target.resource, target.root), () => errorRoot(target.resource, target.root)) .then(modelStat => { @@ -873,20 +815,7 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView ExplorerItem.mergeLocalWithDisk(modelStat, this.model.roots[index]); } - let toExpand: ExplorerItem[] = this.explorerViewer.getExpandedElements().concat(targetsToExpand.map(target => this.model.findClosest(target))); - if (input === this.explorerViewer.getInput()) { - statsToExpand = statsToExpand.concat(toExpand); - if (!delayer.isTriggered()) { - delayerPromise = delayer.trigger(() => this.explorerViewer.refresh() - .then(() => this.explorerViewer.expandAll(statsToExpand)) - .then(() => statsToExpand = []) - ); - } - - return delayerPromise; - } - - return setInputAndExpand(input, statsToExpand); + return this.tree.refresh(null); }))); } @@ -920,34 +849,39 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to * resolve the path from the disk in case the explorer is not yet expanded to the file yet. */ - public select(resource: URI, reveal: boolean = this.autoReveal): TPromise { + select(resource: URI, reveal: boolean = this.autoReveal): void { // Require valid path if (!resource) { - return Promise.resolve(null); + return; } // If path already selected, just reveal and return const selection = this.hasSingleSelection(resource); if (selection) { - return reveal ? this.reveal(selection, 0.5) : Promise.resolve(null); + if (reveal) { + this.reveal(selection, 0.5); + } + + return; } // First try to get the stat object from the input to avoid a roundtrip if (!this.isCreated) { - return Promise.resolve(null); + return; } const fileStat = this.model.findClosest(resource); if (fileStat) { - return this.doSelect(fileStat, reveal); + this.doSelect(fileStat, reveal); + return; } // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.model.roots[0].resource; - return this.fileService.resolveFile(rootUri, options).then(stat => { + this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model const root = this.model.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); @@ -956,77 +890,85 @@ export class ExplorerView extends TreeViewsViewletPanel implements IExplorerView ExplorerItem.mergeLocalWithDisk(modelStat, root); // Select and Reveal - return this.explorerViewer.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); + return this.tree.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); }, e => { this.notificationService.error(e); }); } private hasSingleSelection(resource: URI): ExplorerItem { - const currentSelection: ExplorerItem[] = this.explorerViewer.getSelection(); + const currentSelection: ExplorerItem[] = this.tree.getSelection(); return currentSelection.length === 1 && currentSelection[0].resource.toString() === resource.toString() ? currentSelection[0] : undefined; } - private doSelect(fileStat: ExplorerItem, reveal: boolean): TPromise { + private doSelect(fileStat: ExplorerItem, reveal: boolean): void { if (!fileStat) { - return Promise.resolve(null); + return; } - // Special case: we are asked to reveal and select an element that is not visible - // In this case we take the parent element so that we are at least close to it. - if (!this.filter.isVisible(this.tree, fileStat)) { - fileStat = fileStat.parent; - if (!fileStat) { - return Promise.resolve(null); - } - } + // // Special case: we are asked to reveal and select an element that is not visible + // // In this case we take the parent element so that we are at least close to it. + // if (!this.tree.isVisible(fileStat)) { + // fileStat = fileStat.parent; + // if (!fileStat) { + // return; + // } + // } - // Reveal depending on flag - let revealPromise: TPromise; if (reveal) { - revealPromise = this.reveal(fileStat, 0.5); - } else { - revealPromise = Promise.resolve(null); + this.reveal(fileStat, 0.5); } - return revealPromise.then(() => { - if (!fileStat.isDirectory) { - this.explorerViewer.setSelection([fileStat]); // Since folders can not be opened, only select files - } + if (!fileStat.isDirectory) { + this.tree.setSelection([fileStat]); // Since folders can not be opened, only select files + } - this.explorerViewer.setFocus(fileStat); + this.tree.setFocus([fileStat]); + } + + private reveal(element: any, relativeTop?: number): void { + if (this.tree) { + this.tree.reveal(element, relativeTop); + } + } + + private onContextMenu(e: ITreeContextMenuEvent): void { + const stat = e.element; + if (stat instanceof NewStatPlaceholder) { + return; + } + + // update dynamic contexts + this.fileCopiedContextKey.set(this.clipboardService.hasResources()); + + const selection = this.tree.getSelection(); + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => { + const actions: IAction[] = []; + fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); + return actions; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree.domFocus(); + } + }, + getActionsContext: () => selection && selection.indexOf(stat) >= 0 + ? selection.map((fs: ExplorerItem) => fs.resource) + : stat instanceof ExplorerItem ? [stat.resource] : [] }); } - private reveal(element: any, relativeTop?: number): TPromise { - if (!this.tree) { - return Promise.resolve(null); // return early if viewlet has not yet been created - } - return this.tree.reveal(element, relativeTop); + @memoize private get contributedContextMenu(): IMenu { + const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService); + this.disposables.push(contributedContextMenu); + return contributedContextMenu; } - saveState(): void { - - // Keep list of expanded folders to restore on next load - if (this.isCreated) { - const expanded = this.explorerViewer.getExpandedElements() - .filter(e => e instanceof ExplorerItem) - .map((e: ExplorerItem) => e.resource.toString()); - - if (expanded.length) { - this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES] = expanded; - } else { - delete this.viewState[ExplorerView.MEMENTO_EXPANDED_FOLDER_RESOURCES]; - } - } - - // Clean up last focused if not set - if (!this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]) { - delete this.viewState[ExplorerView.MEMENTO_LAST_ACTIVE_FILE_RESOURCE]; - } - - super.saveState(); + @memoize private get fileCopiedContextKey(): IContextKey { + return FileCopiedContext.bindTo(this.contextKeyService); } dispose(): void { 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 c54fdec00f8..3d41d7fbd57 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -3,112 +3,103 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TPromise } from 'vs/base/common/winjs.base'; -import * as nls from 'vs/nls'; -import * as objects from 'vs/base/common/objects'; +import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import * as DOM from 'vs/base/browser/dom'; -import * as path from 'path'; -import { URI } from 'vs/base/common/uri'; -import { once } from 'vs/base/common/functional'; -import * as paths from 'vs/base/common/paths'; -import * as resources from 'vs/base/common/resources'; -import * as errors from 'vs/base/common/errors'; -import { IAction, ActionRunner as BaseActionRunner, IActionRunner } from 'vs/base/common/actions'; -import * as comparers from 'vs/base/common/comparers'; -import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; -import { isMacintosh, isLinux } from 'vs/base/common/platform'; import * as glob from 'vs/base/common/glob'; -import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/files'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { FileOperationError, FileOperationResult, IFileService, FileKind } from 'vs/platform/files/common/files'; -import { DuplicateFileAction, AddFilesAction, IEditableData, IFileViewletState, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { IDataSource, ITree, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; -import { DesktopDragAndDropData, ExternalElementsDragAndDropData } from 'vs/base/parts/tree/browser/treeDnd'; -import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; -import { ExplorerItem, NewStatPlaceholder, Model } from 'vs/workbench/parts/files/common/explorerModel'; -import { DragMouseEvent, IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IDataSource } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IFileService, FileKind } from 'vs/platform/files/common/files'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProgressService } from 'vs/platform/progress/common/progress'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; -import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; +import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; +import { IFileViewletState, IEditableData } from 'vs/workbench/parts/files/electron-browser/fileActions'; +import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { extractResources, SimpleFileResourceDragAndDrop, CodeDataTransfers, fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; -import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { DataTransfers } from 'vs/base/browser/dnd'; -import { Schemas } from 'vs/base/common/network'; -import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IFilesConfiguration } from 'vs/workbench/parts/files/common/files'; +import { dirname, joinPath, basename } from 'vs/base/common/resources'; +import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; +import { localize } from 'vs/nls'; +import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; +import { once } from 'vs/base/common/functional'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { normalize, join, nativeSep } from 'vs/base/common/paths'; import { rtrim } from 'vs/base/common/strings'; -import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; +import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; +import { equals, deepClone } from 'vs/base/common/objects'; +import * as path from 'path'; + +export class ExplorerDelegate implements IListVirtualDelegate { + + private static readonly ITEM_HEIGHT = 22; + + getHeight(element: ExplorerItem): number { + return ExplorerDelegate.ITEM_HEIGHT; + } + + getTemplateId(element: ExplorerItem): string { + return FilesRenderer.ID; + } +} + +export class ExplorerDataSource implements IDataSource { -export class FileDataSource implements IDataSource { constructor( + private model: Model, @IProgressService private progressService: IProgressService, @INotificationService private notificationService: INotificationService, @IFileService private fileService: IFileService, - @IPartService private partService: IPartService + @IPartService private partService: IPartService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { } - public getId(tree: ITree, stat: ExplorerItem | Model): string { - if (stat instanceof Model) { - return 'model'; - } - - return `${stat.root.resource.toString()}:${stat.getId()}`; + hasChildren(element: ExplorerItem | null): boolean { + return element === null || element.isDirectory; } - public hasChildren(tree: ITree, stat: ExplorerItem | Model): boolean { - return stat instanceof Model || (stat instanceof ExplorerItem && (stat.isDirectory || stat.isRoot)); - } - - public getChildren(tree: ITree, stat: ExplorerItem | Model): TPromise { - if (stat instanceof Model) { - return Promise.resolve(stat.roots); + getChildren(element: ExplorerItem | null): Thenable { + if (element === null) { + if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || this.model.roots[0].isError) { + // Display roots only when multi folder workspace + return Promise.resolve(this.model.roots); + } + element = this.model.roots[0]; } // Return early if stat is already resolved - if (stat.isDirectoryResolved) { - return Promise.resolve(stat.getChildrenArray()); + if (element.isDirectoryResolved) { + return Promise.resolve(element.getChildrenArray()); } // Resolve children and add to fileStat for future lookup else { - // Resolve - const promise = this.fileService.resolveFile(stat.resource, { resolveSingleChildDescendants: true }).then(dirStat => { + const promise = this.fileService.resolveFile(element.resource, { resolveSingleChildDescendants: true }).then(dirStat => { // Convert to view model - const modelDirStat = ExplorerItem.create(dirStat, stat.root); + const modelDirStat = ExplorerItem.create(dirStat, element.root); // Add children to folder const children = modelDirStat.getChildrenArray(); if (children) { children.forEach(child => { - stat.addChild(child); + element.addChild(child); }); } - stat.isDirectoryResolved = true; + element.isDirectoryResolved = true; - return stat.getChildrenArray(); + return element.getChildrenArray(); }, (e: any) => { // Do not show error for roots since we already use an explorer decoration to notify user - if (!(stat instanceof ExplorerItem && stat.isRoot)) { + if (!(element instanceof ExplorerItem && element.isRoot)) { this.notificationService.error(e); } @@ -120,30 +111,15 @@ export class FileDataSource implements IDataSource { return promise; } } - - public getParent(tree: ITree, stat: ExplorerItem | Model): TPromise { - if (!stat) { - return Promise.resolve(null); // can be null if nothing selected in the tree - } - - // Return if root reached - if (tree.getInput() === stat) { - return Promise.resolve(null); - } - - // Return if parent already resolved - if (stat instanceof ExplorerItem && stat.parent) { - return Promise.resolve(stat.parent); - } - - // We never actually resolve the parent from the disk for performance reasons. It wouldnt make - // any sense to resolve parent by parent with requests to walk up the chain. Instead, the explorer - // makes sure to properly resolve a deep path to a specific file and merges the result with the model. - return Promise.resolve(null); - } } -export class FileViewletState implements IFileViewletState { +export interface IFileTemplateData { + elementDisposable: IDisposable; + label: FileLabel; + container: HTMLElement; +} + +export class EditableExplorerItems implements IFileViewletState { private editableStats: Map; constructor() { @@ -165,38 +141,16 @@ export class FileViewletState implements IFileViewletState { } } -export class ActionRunner extends BaseActionRunner implements IActionRunner { - private viewletState: FileViewletState; +export class FilesRenderer implements ITreeRenderer, IDisposable { + static readonly ID = 'file'; - constructor(state: FileViewletState) { - super(); - - this.viewletState = state; - } - - public run(action: IAction, context?: any): TPromise { - return super.run(action, { viewletState: this.viewletState }); - } -} - -export interface IFileTemplateData { - elementDisposable: IDisposable; - label: FileLabel; - container: HTMLElement; -} - -// Explorer Renderer -export class FileRenderer implements IRenderer { - - private static readonly ITEM_HEIGHT = 22; - private static readonly FILE_TEMPLATE_ID = 'file'; - - private state: FileViewletState; + private state: EditableExplorerItems; private config: IFilesConfiguration; private configListener: IDisposable; + private tree: WorkbenchAsyncDataTree; constructor( - state: FileViewletState, + state: EditableExplorerItems, @IContextViewService private contextViewService: IContextViewService, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @@ -213,33 +167,24 @@ export class FileRenderer implements IRenderer { }); } - dispose(): void { - this.configListener.dispose(); + get templateId(): string { + return FilesRenderer.ID; } - public getHeight(tree: ITree, element: any): number { - return FileRenderer.ITEM_HEIGHT; + acquireTree(tree: WorkbenchAsyncDataTree): void { + this.tree = tree; } - public getTemplateId(tree: ITree, element: any): string { - return FileRenderer.FILE_TEMPLATE_ID; - } - - public disposeTemplate(tree: ITree, templateId: string, templateData: IFileTemplateData): void { - templateData.elementDisposable.dispose(); - templateData.label.dispose(); - } - - public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): IFileTemplateData { + renderTemplate(container: HTMLElement): IFileTemplateData { const elementDisposable = Disposable.None; const label = this.instantiationService.createInstance(FileLabel, container, void 0); return { elementDisposable, label, container }; } - public renderElement(tree: ITree, stat: ExplorerItem, templateId: string, templateData: IFileTemplateData): void { + renderElement(element: ITreeNode, index: number, templateData: IFileTemplateData): void { templateData.elementDisposable.dispose(); - + const stat = element.element; const editableData: IEditableData = this.state.getEditableData(stat); // File Label @@ -254,19 +199,20 @@ export class FileRenderer implements IRenderer { }); templateData.elementDisposable = templateData.label.onDidRender(() => { - tree.updateWidth(stat); + // todo@isidor horizontal scrolling + // this.tree.updateWidth(stat); }); } // Input Box else { templateData.label.element.style.display = 'none'; - this.renderInputBox(templateData.container, tree, stat, editableData); + this.renderInputBox(templateData.container, stat, editableData); templateData.elementDisposable = Disposable.None; } } - private renderInputBox(container: HTMLElement, tree: ITree, stat: ExplorerItem, editableData: IEditableData): void { + private renderInputBox(container: HTMLElement, stat: ExplorerItem, editableData: IEditableData): void { // Use a file label only for the icon next to the input box const label = this.instantiationService.createInstance(FileLabel, container, void 0); @@ -274,22 +220,22 @@ export class FileRenderer implements IRenderer { const fileKind = stat.isRoot ? FileKind.ROOT_FOLDER : (stat.isDirectory || (stat instanceof NewStatPlaceholder && stat.isDirectoryPlaceholder())) ? FileKind.FOLDER : FileKind.FILE; const labelOptions: IFileLabelOptions = { hidePath: true, hideLabel: true, fileKind, extraClasses }; - const parent = stat.name ? resources.dirname(stat.resource) : stat.resource; + const parent = stat.name ? dirname(stat.resource) : stat.resource; const value = stat.name || ''; - label.setFile(resources.joinPath(parent, value || ' '), labelOptions); // Use icon for ' ' if name is empty. + label.setFile(joinPath(parent, value || ' '), labelOptions); // Use icon for ' ' if name is empty. // Input field for name const inputBox = new InputBox(label.element, this.contextViewService, { validationOptions: { validation: editableData.validator }, - ariaLabel: nls.localize('fileInputAriaLabel', "Type file name. Press Enter to confirm or Escape to cancel.") + ariaLabel: localize('fileInputAriaLabel', "Type file name. Press Enter to confirm or Escape to cancel.") }); const styler = attachInputBoxStyler(inputBox, this.themeService); inputBox.onDidChange(value => { - label.setFile(resources.joinPath(parent, value || ' '), labelOptions); // update label icon while typing! + label.setFile(joinPath(parent, value || ' '), labelOptions); // update label icon while typing! }); const lastDot = value.lastIndexOf('.'); @@ -299,20 +245,17 @@ export class FileRenderer implements IRenderer { inputBox.focus(); const done = once((commit: boolean, blur: boolean) => { - tree.clearHighlight(); label.element.style.display = 'none'; + if (stat instanceof NewStatPlaceholder) { + stat.destroy(); + } if (commit && inputBox.value) { editableData.action.run({ value: inputBox.value }); } - - setTimeout(() => { - if (!blur) { // https://github.com/Microsoft/vscode/issues/20269 - tree.domFocus(); - } - dispose(toDispose); - container.removeChild(label.element); - }, 0); + dispose(toDispose); + container.removeChild(label.element); + this.tree.refresh(stat.parent).then(() => this.tree.domFocus()); }); const toDispose = [ @@ -330,7 +273,7 @@ export class FileRenderer implements IRenderer { const initialRelPath: string = path.relative(stat.root.resource.path, stat.parent.resource.path); let projectFolderName: string = ''; if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - projectFolderName = paths.basename(stat.root.resource.path); // show root folder name in multi-folder project + projectFolderName = basename(stat.root.resource); // show root folder name in multi-folder project } this.showInputMessage(inputBox, initialRelPath, projectFolderName, editableData.action.id); }), @@ -346,24 +289,24 @@ export class FileRenderer implements IRenderer { if (inputBox.validate()) { const value = inputBox.value; if (value && /.[\\/]./.test(value)) { // only show if there's at least one slash enclosed in the string - let displayPath = path.normalize(path.join(projectFolderName, initialRelPath, value)); - displayPath = rtrim(displayPath, paths.nativeSep); + let displayPath = normalize(join(projectFolderName, initialRelPath, value)); + displayPath = rtrim(displayPath, nativeSep); - const indexLastSlash: number = displayPath.lastIndexOf(paths.nativeSep); + const indexLastSlash: number = displayPath.lastIndexOf(nativeSep); const name: string = displayPath.substring(indexLastSlash + 1); const leadingPathPart: string = displayPath.substring(0, indexLastSlash); let msg: string; switch (actionID) { case 'workbench.files.action.createFileFromExplorer': - msg = nls.localize('createFileFromExplorerInfoMessage', "Create file **{0}** in **{1}**", name, leadingPathPart); + msg = localize('createFileFromExplorerInfoMessage', "Create file **{0}** in **{1}**", name, leadingPathPart); break; case 'workbench.files.action.renameFile': - msg = nls.localize('renameFileFromExplorerInfoMessage', "Move and rename to **{0}**", displayPath); + msg = localize('renameFileFromExplorerInfoMessage', "Move and rename to **{0}**", displayPath); break; case 'workbench.files.action.createFolderFromExplorer': // fallthrough default: - msg = nls.localize('createFolderFromExplorerInfoMessage', "Create folder **{0}** in **{1}**", name, leadingPathPart); + msg = localize('createFolderFromExplorerInfoMessage', "Create folder **{0}** in **{1}**", name, leadingPathPart); } inputBox.showMessage({ @@ -373,7 +316,7 @@ export class FileRenderer implements IRenderer { }); } else if (value && /^\s|\s$/.test(value)) { inputBox.showMessage({ - content: nls.localize('whitespace', "Leading or trailing whitespace detected"), + content: localize('whitespace', "Leading or trailing whitespace detected"), formatContent: true, type: MessageType.WARNING }); @@ -382,322 +325,33 @@ export class FileRenderer implements IRenderer { } } } -} -// Explorer Accessibility Provider -export class FileAccessibilityProvider implements IAccessibilityProvider { + disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { + // noop + } - public getAriaLabel(tree: ITree, stat: ExplorerItem): string { - return stat.name; + disposeTemplate(templateData: IFileTemplateData): void { + templateData.elementDisposable.dispose(); + templateData.label.dispose(); + } + + dispose(): void { + this.configListener.dispose(); } } -// Explorer Controller -export class FileController extends WorkbenchTreeController implements IDisposable { - private fileCopiedContextKey: IContextKey; - private contributedContextMenu: IMenu; - private toDispose: IDisposable[]; - private previousSelectionRangeStop: ExplorerItem; - - constructor( - @IEditorService private editorService: IEditorService, - @IContextMenuService private contextMenuService: IContextMenuService, - @ITelemetryService private telemetryService: ITelemetryService, - @IMenuService private menuService: IMenuService, - @IContextKeyService contextKeyService: IContextKeyService, - @IClipboardService private clipboardService: IClipboardService, - @IConfigurationService configurationService: IConfigurationService - ) { - super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */ }, configurationService); - - this.fileCopiedContextKey = FileCopiedContext.bindTo(contextKeyService); - this.toDispose = []; - } - - public onLeftClick(tree: WorkbenchTree, stat: ExplorerItem | Model, event: IMouseEvent, origin: string = 'mouse'): boolean { - const payload = { origin: origin }; - const isDoubleClick = (origin === 'mouse' && event.detail === 2); - - // Handle Highlight Mode - if (tree.getHighlight()) { - - // Cancel Event - event.preventDefault(); - event.stopPropagation(); - - tree.clearHighlight(payload); - - return false; - } - - // Handle root - if (stat instanceof Model) { - tree.clearFocus(payload); - tree.clearSelection(payload); - - return false; - } - - // Cancel Event - const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; - if (!isMouseDown) { - event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise - } - event.stopPropagation(); - - // Set DOM focus - tree.domFocus(); - if (stat instanceof NewStatPlaceholder) { - return true; - } - - // Allow to multiselect - if ((tree.useAltAsMultipleSelectionModifier && event.altKey) || !tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)) { - const selection = tree.getSelection(); - this.previousSelectionRangeStop = undefined; - if (selection.indexOf(stat) >= 0) { - tree.setSelection(selection.filter(s => s !== stat)); - } else { - tree.setSelection(selection.concat(stat)); - tree.setFocus(stat, payload); - } - } - - // Allow to unselect - else if (event.shiftKey) { - const focus = tree.getFocus(); - if (focus) { - if (this.previousSelectionRangeStop) { - tree.deselectRange(stat, this.previousSelectionRangeStop); - } - tree.selectRange(focus, stat, payload); - this.previousSelectionRangeStop = stat; - } - } - - // Select, Focus and open files - else { - - // Expand / Collapse - if (isDoubleClick || this.openOnSingleClick || this.isClickOnTwistie(event)) { - tree.toggleExpansion(stat, event.altKey); - this.previousSelectionRangeStop = undefined; - } - - const preserveFocus = !isDoubleClick; - tree.setFocus(stat, payload); - - if (isDoubleClick) { - event.preventDefault(); // focus moves to editor, we need to prevent default - } - - tree.setSelection([stat], payload); - - if (!stat.isDirectory && (isDoubleClick || this.openOnSingleClick)) { - let sideBySide = false; - if (event) { - sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; - } - - this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick }); - } - } - - return true; - } - - public onMouseMiddleClick(tree: WorkbenchTree, element: ExplorerItem | Model, event: IMouseEvent): boolean { - let sideBySide = false; - if (event) { - sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; - } - if (element instanceof ExplorerItem && !element.isDirectory) { - this.openEditor(element, { preserveFocus: true, sideBySide, pinned: true }); - } - - return true; - } - - public onContextMenu(tree: WorkbenchTree, stat: ExplorerItem | Model, event: ContextMenuEvent): boolean { - if (event.target && event.target.tagName && event.target.tagName.toLowerCase() === 'input') { - return false; - } - - event.preventDefault(); - event.stopPropagation(); - - tree.setFocus(stat); - - // update dynamic contexts - this.fileCopiedContextKey.set(this.clipboardService.hasResources()); - - if (!this.contributedContextMenu) { - this.contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, tree.contextKeyService); - this.toDispose.push(this.contributedContextMenu); - } - - const anchor = { x: event.posx, y: event.posy }; - const selection = tree.getSelection(); - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => { - const actions: IAction[] = []; - fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); - return actions; - }, - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - tree.domFocus(); - } - }, - getActionsContext: () => selection && selection.indexOf(stat) >= 0 - ? selection.map((fs: ExplorerItem) => fs.resource) - : stat instanceof ExplorerItem ? [stat.resource] : [] - }); - - return true; - } - - public openEditor(stat: ExplorerItem, options: { preserveFocus: boolean; sideBySide: boolean; pinned: boolean; }): void { - if (stat && !stat.isDirectory) { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - - this.editorService.openEditor({ resource: stat.resource, options }, options.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); - } - } - - public dispose(): void { - this.toDispose = dispose(this.toDispose); +export class ExplorerAccessibilityProvider implements IAccessibilityProvider { + getAriaLabel(element: ExplorerItem): string { + return element.name; } } -// Explorer Sorter -export class FileSorter implements ISorter { - private toDispose: IDisposable[]; - private sortOrder: SortOrder; - - constructor( - @IConfigurationService private configurationService: IConfigurationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - ) { - this.toDispose = []; - - this.updateSortOrder(); - - this.registerListeners(); - } - - private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateSortOrder())); - } - - private updateSortOrder(): void { - this.sortOrder = this.configurationService.getValue('explorer.sortOrder') || 'default'; - } - - public compare(tree: ITree, statA: ExplorerItem, statB: ExplorerItem): number { - - // Do not sort roots - if (statA.isRoot) { - if (statB.isRoot) { - return this.contextService.getWorkspaceFolder(statA.resource).index - this.contextService.getWorkspaceFolder(statB.resource).index; - } - - return -1; - } - - if (statB.isRoot) { - return 1; - } - - // Sort Directories - switch (this.sortOrder) { - case 'type': - if (statA.isDirectory && !statB.isDirectory) { - return -1; - } - - if (statB.isDirectory && !statA.isDirectory) { - return 1; - } - - if (statA.isDirectory && statB.isDirectory) { - return comparers.compareFileNames(statA.name, statB.name); - } - - break; - - case 'filesFirst': - if (statA.isDirectory && !statB.isDirectory) { - return 1; - } - - if (statB.isDirectory && !statA.isDirectory) { - return -1; - } - - break; - - case 'mixed': - break; // not sorting when "mixed" is on - - default: /* 'default', 'modified' */ - if (statA.isDirectory && !statB.isDirectory) { - return -1; - } - - if (statB.isDirectory && !statA.isDirectory) { - return 1; - } - - break; - } - - // Sort "New File/Folder" placeholders - if (statA instanceof NewStatPlaceholder) { - return -1; - } - - if (statB instanceof NewStatPlaceholder) { - return 1; - } - - // Sort Files - switch (this.sortOrder) { - case 'type': - return comparers.compareFileExtensions(statA.name, statB.name); - - case 'modified': - if (statA.mtime !== statB.mtime) { - return statA.mtime < statB.mtime ? 1 : -1; - } - - return comparers.compareFileNames(statA.name, statB.name); - - default: /* 'default', 'mixed', 'filesFirst' */ - return comparers.compareFileNames(statA.name, statB.name); - } - } - - public dispose(): void { - this.toDispose = dispose(this.toDispose); - } -} - -// Explorer Filter interface CachedParsedExpression { original: glob.IExpression; parsed: glob.ParsedExpression; } -export class FileFilter implements IFilter { - +export class FilesFilter implements ITreeFilter { private hiddenExpressionPerRoot: Map; private workspaceFolderChangeListener: IDisposable; @@ -706,15 +360,10 @@ export class FileFilter implements IFilter { @IConfigurationService private configurationService: IConfigurationService ) { this.hiddenExpressionPerRoot = new Map(); - - this.registerListeners(); - } - - public registerListeners(): void { this.workspaceFolderChangeListener = this.contextService.onDidChangeWorkspaceFolders(() => this.updateConfiguration()); } - public updateConfiguration(): boolean { + updateConfiguration(): boolean { let needsRefresh = false; this.contextService.getWorkspace().folders.forEach(folder => { const configuration = this.configurationService.getValue({ resource: folder.uri }); @@ -722,10 +371,10 @@ export class FileFilter implements IFilter { if (!needsRefresh) { const cached = this.hiddenExpressionPerRoot.get(folder.uri.toString()); - needsRefresh = !cached || !objects.equals(cached.original, excludesConfig); + needsRefresh = !cached || !equals(cached.original, excludesConfig); } - const excludesConfigCopy = objects.deepClone(excludesConfig); // do not keep the config, as it gets mutated under our hoods + const excludesConfigCopy = deepClone(excludesConfig); // do not keep the config, as it gets mutated under our hoods this.hiddenExpressionPerRoot.set(folder.uri.toString(), { original: excludesConfigCopy, parsed: glob.parse(excludesConfigCopy) } as CachedParsedExpression); }); @@ -733,18 +382,17 @@ export class FileFilter implements IFilter { return needsRefresh; } - public isVisible(tree: ITree, stat: ExplorerItem): boolean { - return this.doIsVisible(stat); - } - - private doIsVisible(stat: ExplorerItem): boolean { + filter(stat: ExplorerItem, parentVisibility: TreeVisibility): TreeFilterResult { + if (parentVisibility === TreeVisibility.Hidden) { + return false; + } if (stat instanceof NewStatPlaceholder || stat.isRoot) { return true; // always visible } // Hide those that match Hidden Patterns const cached = this.hiddenExpressionPerRoot.get(stat.root.resource.toString()); - if (cached && cached.parsed(paths.normalize(path.relative(stat.root.resource.path, stat.resource.path), true), stat.name, name => !!stat.parent.getChild(name))) { + if (cached && cached.parsed(normalize(path.relative(stat.root.resource.path, stat.resource.path), true), stat.name, name => !!stat.parent.getChild(name))) { return false; // hidden through pattern } @@ -756,346 +404,597 @@ export class FileFilter implements IFilter { } } -// Explorer Drag And Drop Controller -export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { - - private static readonly CONFIRM_DND_SETTING_KEY = 'explorer.confirmDragAndDrop'; - - private toDispose: IDisposable[]; - private dropEnabled: boolean; - - constructor( - @INotificationService private notificationService: INotificationService, - @IDialogService private dialogService: IDialogService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IFileService private fileService: IFileService, - @IConfigurationService private configurationService: IConfigurationService, - @IInstantiationService instantiationService: IInstantiationService, - @ITextFileService private textFileService: ITextFileService, - @IWindowService private windowService: IWindowService, - @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService - ) { - super(stat => this.statToResource(stat), instantiationService); - - this.toDispose = []; - - this.updateDropEnablement(); - - this.registerListeners(); - } - - private statToResource(stat: ExplorerItem): URI { - if (stat.isDirectory) { - return URI.from({ scheme: 'folder', path: stat.resource.path }); // indicates that we are dragging a folder - } - - return stat.resource; - } - - private registerListeners(): void { - this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateDropEnablement())); - } - - private updateDropEnablement(): void { - this.dropEnabled = this.configurationService.getValue('explorer.enableDragAndDrop'); - } - - public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { - const sources: ExplorerItem[] = data.getData(); - if (sources && sources.length) { - - // When dragging folders, make sure to collapse them to free up some space - sources.forEach(s => { - if (s.isDirectory && tree.isExpanded(s)) { - tree.collapse(s, false); - } - }); - - // Apply some datatransfer types to allow for dragging the element outside of the application - this.instantiationService.invokeFunction(fillResourceDataTransfers, sources, originalEvent); - - // The only custom data transfer we set from the explorer is a file transfer - // to be able to DND between multiple code file explorers across windows - const fileResources = sources.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); - if (fileResources.length) { - originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); - } - } - } - - public onDragOver(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): IDragOverReaction { - if (!this.dropEnabled) { - return DRAG_OVER_REJECT; - } - - const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); - const fromDesktop = data instanceof DesktopDragAndDropData; - - // Desktop DND - if (fromDesktop) { - const types: string[] = originalEvent.dataTransfer.types; - const typesArray: string[] = []; - for (let i = 0; i < types.length; i++) { - typesArray.push(types[i].toLowerCase()); // somehow the types are lowercase - } - - if (typesArray.indexOf(DataTransfers.FILES.toLowerCase()) === -1 && typesArray.indexOf(CodeDataTransfers.FILES.toLowerCase()) === -1) { - return DRAG_OVER_REJECT; - } - } - - // Other-Tree DND - else if (data instanceof ExternalElementsDragAndDropData) { - return DRAG_OVER_REJECT; - } - - // In-Explorer DND - else { - const sources: ExplorerItem[] = data.getData(); - if (target instanceof Model) { - if (sources[0].isRoot) { - return DRAG_OVER_ACCEPT_BUBBLE_DOWN(false); - } - - return DRAG_OVER_REJECT; - } - - if (!Array.isArray(sources)) { - return DRAG_OVER_REJECT; - } - - if (sources.some((source) => { - if (source instanceof NewStatPlaceholder) { - return true; // NewStatPlaceholders can not be moved - } - - if (source.isRoot && target instanceof ExplorerItem && !target.isRoot) { - return true; // Root folder can not be moved to a non root file stat. - } - - if (source.resource.toString() === target.resource.toString()) { - return true; // Can not move anything onto itself - } - - if (source.isRoot && target instanceof ExplorerItem && target.isRoot) { - // Disable moving workspace roots in one another - return false; - } - - if (!isCopy && resources.dirname(source.resource).toString() === target.resource.toString()) { - return true; // Can not move a file to the same parent unless we copy - } - - if (resources.isEqualOrParent(target.resource, source.resource, !isLinux /* ignorecase */)) { - return true; // Can not move a parent folder into one of its children - } - - return false; - })) { - return DRAG_OVER_REJECT; - } - } - - // All (target = model) - if (target instanceof Model) { - return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace - } - - // All (target = file/folder) - else { - if (target.isDirectory) { - if (target.isReadonly) { - return DRAG_OVER_REJECT; - } - return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); - } - - if (this.contextService.getWorkspace().folders.every(folder => folder.uri.toString() !== target.resource.toString())) { - return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; - } - } - - return DRAG_OVER_REJECT; - } - - public drop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): void { - - // Desktop DND (Import file) - if (data instanceof DesktopDragAndDropData) { - this.handleExternalDrop(tree, data, target, originalEvent); - } - - // In-Explorer DND (Move/Copy file) - else { - this.handleExplorerDrop(tree, data, target, originalEvent); - } - } - - private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { - const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); - - // Check for dropped external files to be folders - return this.fileService.resolveFiles(droppedResources).then(result => { - - // Pass focus to window - this.windowService.focusWindow(); - - // Handle folders by adding to workspace if we are in workspace context - const folders = result.filter(r => r.success && r.stat.isDirectory).map(result => ({ uri: result.stat.resource })); - if (folders.length > 0) { - - // If we are in no-workspace context, ask for confirmation to create a workspace - let confirmedPromise: TPromise = Promise.resolve({ confirmed: true }); - if (this.contextService.getWorkbenchState() !== WorkbenchState.WORKSPACE) { - confirmedPromise = this.dialogService.confirm({ - message: folders.length > 1 ? nls.localize('dropFolders', "Do you want to add the folders to the workspace?") : nls.localize('dropFolder', "Do you want to add the folder to the workspace?"), - type: 'question', - primaryButton: folders.length > 1 ? nls.localize('addFolders', "&&Add Folders") : nls.localize('addFolder', "&&Add Folder") - }); - } - - return confirmedPromise.then(res => { - if (res.confirmed) { - return this.workspaceEditingService.addFolders(folders); - } - - return void 0; - }); - } - - // Handle dropped files (only support FileStat as target) - else if (target instanceof ExplorerItem && !target.isReadonly) { - const addFilesAction = this.instantiationService.createInstance(AddFilesAction, tree, target, null); - - return addFilesAction.run(droppedResources.map(res => res.resource)); - } - - return void 0; - }); - } - - private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { - const sources: ExplorerItem[] = resources.distinctParents(data.getData(), s => s.resource); - const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); - - let confirmPromise: TPromise; - - // Handle confirm setting - const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); - if (confirmDragAndDrop) { - confirmPromise = this.dialogService.confirm({ - message: sources.length > 1 && sources.every(s => s.isRoot) ? nls.localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") - : sources.length > 1 ? getConfirmMessage(nls.localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", sources.length), sources.map(s => s.resource)) - : sources[0].isRoot ? nls.localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", sources[0].name) - : nls.localize('confirmMove', "Are you sure you want to move '{0}'?", sources[0].name), - checkbox: { - label: nls.localize('doNotAskAgain', "Do not ask me again") - }, - type: 'question', - primaryButton: nls.localize({ key: 'moveButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Move") - }); - } else { - confirmPromise = Promise.resolve({ confirmed: true } as IConfirmationResult); - } - - return confirmPromise.then(res => { - - // Check for confirmation checkbox - let updateConfirmSettingsPromise: TPromise = Promise.resolve(void 0); - if (res.confirmed && res.checkboxChecked === true) { - updateConfirmSettingsPromise = this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); - } - - return updateConfirmSettingsPromise.then(() => { - if (res.confirmed) { - const rootDropPromise = this.doHandleRootDrop(sources.filter(s => s.isRoot), target); - return Promise.all(sources.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(tree, source, target, isCopy)).concat(rootDropPromise)).then(() => void 0); - } - - return Promise.resolve(void 0); - }); - }); - } - - private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem | Model): TPromise { - if (roots.length === 0) { - return Promise.resolve(undefined); - } - - const folders = this.contextService.getWorkspace().folders; - let targetIndex: number; - const workspaceCreationData: IWorkspaceFolderCreationData[] = []; - const rootsToMove: IWorkspaceFolderCreationData[] = []; - - for (let index = 0; index < folders.length; index++) { - const data = { - uri: folders[index].uri - }; - if (target instanceof ExplorerItem && folders[index].uri.toString() === target.resource.toString()) { - targetIndex = workspaceCreationData.length; - } - - if (roots.every(r => r.resource.toString() !== folders[index].uri.toString())) { - workspaceCreationData.push(data); - } else { - rootsToMove.push(data); - } - } - if (target instanceof Model) { - targetIndex = workspaceCreationData.length; - } - - workspaceCreationData.splice(targetIndex, 0, ...rootsToMove); - return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); - } - - private doHandleExplorerDrop(tree: ITree, source: ExplorerItem, target: ExplorerItem | Model, isCopy: boolean): TPromise { - if (!(target instanceof ExplorerItem)) { - return Promise.resolve(void 0); - } - - return tree.expand(target).then(() => { - - if (target.isReadonly) { - return void 0; - } - - // Reuse duplicate action if user copies - if (isCopy) { - return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run(); - } - - // Otherwise move - const targetResource = resources.joinPath(target.resource, source.name); - - return this.textFileService.move(source.resource, targetResource).then(void 0, error => { - - // Conflict - if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { - const confirm: IConfirmation = { - message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), - detail: nls.localize('irreversible', "This action is irreversible!"), - primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' - }; - - // Move with overwrite if the user confirms - return this.dialogService.confirm(confirm).then(res => { - if (res.confirmed) { - return this.textFileService.move(source.resource, targetResource, true /* overwrite */).then(void 0, error => this.notificationService.error(error)); - } - - return void 0; - }); - } - - // Any other error - else { - this.notificationService.error(error); - } - - return void 0; - }); - }, errors.onUnexpectedError); - } -} +// Explorer Controller +// export class FileController extends WorkbenchTreeController implements IDisposable { +// private fileCopiedContextKey: IContextKey; +// private contributedContextMenu: IMenu; +// private previousSelectionRangeStop: ExplorerItem; + +// constructor( +// @IEditorService private editorService: IEditorService, +// @IContextMenuService private contextMenuService: IContextMenuService, +// @ITelemetryService private telemetryService: ITelemetryService, +// @IMenuService private menuService: IMenuService, +// @IContextKeyService contextKeyService: IContextKeyService, +// @IClipboardService private clipboardService: IClipboardService, +// @IConfigurationService configurationService: IConfigurationService +// ) { +// super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */ }, configurationService); + +// this.fileCopiedContextKey = FileCopiedContext.bindTo(contextKeyService); +// } + +// public onLeftClick(tree: WorkbenchTree, stat: ExplorerItem | Model, event: IMouseEvent, origin: string = 'mouse'): boolean { +// const payload = { origin: origin }; +// const isDoubleClick = (origin === 'mouse' && event.detail === 2); + +// // Handle Highlight Mode +// if (tree.getHighlight()) { + +// // Cancel Event +// event.preventDefault(); +// event.stopPropagation(); + +// tree.clearHighlight(payload); + +// return false; +// } + +// // Handle root +// if (stat instanceof Model) { +// tree.clearFocus(payload); +// tree.clearSelection(payload); + +// return false; +// } + +// // Cancel Event +// const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; +// if (!isMouseDown) { +// event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise +// } +// event.stopPropagation(); + +// // Set DOM focus +// tree.domFocus(); +// if (stat instanceof NewStatPlaceholder) { +// return true; +// } + +// // Allow to multiselect +// if ((tree.useAltAsMultipleSelectionModifier && event.altKey) || !tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)) { +// const selection = tree.getSelection(); +// this.previousSelectionRangeStop = undefined; +// if (selection.indexOf(stat) >= 0) { +// tree.setSelection(selection.filter(s => s !== stat)); +// } else { +// tree.setSelection(selection.concat(stat)); +// tree.setFocus(stat, payload); +// } +// } + +// // Allow to unselect +// else if (event.shiftKey) { +// const focus = tree.getFocus(); +// if (focus) { +// if (this.previousSelectionRangeStop) { +// tree.deselectRange(stat, this.previousSelectionRangeStop); +// } +// tree.selectRange(focus, stat, payload); +// this.previousSelectionRangeStop = stat; +// } +// } + +// // Select, Focus and open files +// else { + +// // Expand / Collapse +// if (isDoubleClick || this.openOnSingleClick || this.isClickOnTwistie(event)) { +// tree.toggleExpansion(stat, event.altKey); +// this.previousSelectionRangeStop = undefined; +// } + +// const preserveFocus = !isDoubleClick; +// tree.setFocus(stat, payload); + +// if (isDoubleClick) { +// event.preventDefault(); // focus moves to editor, we need to prevent default +// } + +// tree.setSelection([stat], payload); + +// if (!stat.isDirectory && (isDoubleClick || this.openOnSingleClick)) { +// let sideBySide = false; +// if (event) { +// sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; +// } + +// this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick }); +// } +// } + +// return true; +// } + +// public onMouseMiddleClick(tree: WorkbenchTree, element: ExplorerItem | Model, event: IMouseEvent): boolean { +// let sideBySide = false; +// if (event) { +// sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; +// } +// if (element instanceof ExplorerItem && !element.isDirectory) { +// this.openEditor(element, { preserveFocus: true, sideBySide, pinned: true }); +// } + +// return true; +// } + +// public openEditor(stat: ExplorerItem, options: { preserveFocus: boolean; sideBySide: boolean; pinned: boolean; }): void { +// if (stat && !stat.isDirectory) { +// /* __GDPR__ +// "workbenchActionExecuted" : { +// "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, +// "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } +// } +// */ +// this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); + +// this.editorService.openEditor({ resource: stat.resource, options }, options.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); +// } +// } +// } + +// // Explorer Sorter +// export class FileSorter implements ISorter { +// private toDispose: IDisposable[]; +// private sortOrder: SortOrder; + +// constructor( +// @IConfigurationService private configurationService: IConfigurationService, +// @IWorkspaceContextService private contextService: IWorkspaceContextService +// ) { +// this.toDispose = []; + +// this.updateSortOrder(); + +// this.registerListeners(); +// } + +// private registerListeners(): void { +// this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateSortOrder())); +// } + +// private updateSortOrder(): void { +// this.sortOrder = this.configurationService.getValue('explorer.sortOrder') || 'default'; +// } + +// public compare(tree: ITree, statA: ExplorerItem, statB: ExplorerItem): number { + +// // Do not sort roots +// if (statA.isRoot) { +// if (statB.isRoot) { +// return this.contextService.getWorkspaceFolder(statA.resource).index - this.contextService.getWorkspaceFolder(statB.resource).index; +// } + +// return -1; +// } + +// if (statB.isRoot) { +// return 1; +// } + +// // Sort Directories +// switch (this.sortOrder) { +// case 'type': +// if (statA.isDirectory && !statB.isDirectory) { +// return -1; +// } + +// if (statB.isDirectory && !statA.isDirectory) { +// return 1; +// } + +// if (statA.isDirectory && statB.isDirectory) { +// return comparers.compareFileNames(statA.name, statB.name); +// } + +// break; + +// case 'filesFirst': +// if (statA.isDirectory && !statB.isDirectory) { +// return 1; +// } + +// if (statB.isDirectory && !statA.isDirectory) { +// return -1; +// } + +// break; + +// case 'mixed': +// break; // not sorting when "mixed" is on + +// default: /* 'default', 'modified' */ +// if (statA.isDirectory && !statB.isDirectory) { +// return -1; +// } + +// if (statB.isDirectory && !statA.isDirectory) { +// return 1; +// } + +// break; +// } + +// // Sort "New File/Folder" placeholders +// if (statA instanceof NewStatPlaceholder) { +// return -1; +// } + +// if (statB instanceof NewStatPlaceholder) { +// return 1; +// } + +// // Sort Files +// switch (this.sortOrder) { +// case 'type': +// return comparers.compareFileExtensions(statA.name, statB.name); + +// case 'modified': +// if (statA.mtime !== statB.mtime) { +// return statA.mtime < statB.mtime ? 1 : -1; +// } + +// return comparers.compareFileNames(statA.name, statB.name); + +// default: /* 'default', 'mixed', 'filesFirst' */ +// return comparers.compareFileNames(statA.name, statB.name); +// } +// } + +// public dispose(): void { +// this.toDispose = dispose(this.toDispose); +// } +// } + +// export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { + +// private static readonly CONFIRM_DND_SETTING_KEY = 'explorer.confirmDragAndDrop'; + +// private toDispose: IDisposable[]; +// private dropEnabled: boolean; + +// constructor( +// @INotificationService private notificationService: INotificationService, +// @IDialogService private dialogService: IDialogService, +// @IWorkspaceContextService private contextService: IWorkspaceContextService, +// @IFileService private fileService: IFileService, +// @IConfigurationService private configurationService: IConfigurationService, +// @IInstantiationService instantiationService: IInstantiationService, +// @ITextFileService private textFileService: ITextFileService, +// @IWindowService private windowService: IWindowService, +// @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService +// ) { +// super(stat => this.statToResource(stat), instantiationService); + +// this.toDispose = []; + +// this.updateDropEnablement(); + +// this.registerListeners(); +// } + +// private statToResource(stat: ExplorerItem): URI { +// if (stat.isDirectory) { +// return URI.from({ scheme: 'folder', path: stat.resource.path }); // indicates that we are dragging a folder +// } + +// return stat.resource; +// } + +// private registerListeners(): void { +// this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateDropEnablement())); +// } + +// private updateDropEnablement(): void { +// this.dropEnabled = this.configurationService.getValue('explorer.enableDragAndDrop'); +// } + +// public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { +// const sources: ExplorerItem[] = data.getData(); +// if (sources && sources.length) { + +// // When dragging folders, make sure to collapse them to free up some space +// sources.forEach(s => { +// if (s.isDirectory && tree.isExpanded(s)) { +// tree.collapse(s, false); +// } +// }); + +// // Apply some datatransfer types to allow for dragging the element outside of the application +// this.instantiationService.invokeFunction(fillResourceDataTransfers, sources, originalEvent); + +// // The only custom data transfer we set from the explorer is a file transfer +// // to be able to DND between multiple code file explorers across windows +// const fileResources = sources.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); +// if (fileResources.length) { +// originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); +// } +// } +// } + +// public onDragOver(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): IDragOverReaction { +// if (!this.dropEnabled) { +// return DRAG_OVER_REJECT; +// } + +// const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); +// const fromDesktop = data instanceof DesktopDragAndDropData; + +// // Desktop DND +// if (fromDesktop) { +// const types: string[] = originalEvent.dataTransfer.types; +// const typesArray: string[] = []; +// for (let i = 0; i < types.length; i++) { +// typesArray.push(types[i].toLowerCase()); // somehow the types are lowercase +// } + +// if (typesArray.indexOf(DataTransfers.FILES.toLowerCase()) === -1 && typesArray.indexOf(CodeDataTransfers.FILES.toLowerCase()) === -1) { +// return DRAG_OVER_REJECT; +// } +// } + +// // Other-Tree DND +// else if (data instanceof ExternalElementsDragAndDropData) { +// return DRAG_OVER_REJECT; +// } + +// // In-Explorer DND +// else { +// const sources: ExplorerItem[] = data.getData(); +// if (target instanceof Model) { +// if (sources[0].isRoot) { +// return DRAG_OVER_ACCEPT_BUBBLE_DOWN(false); +// } + +// return DRAG_OVER_REJECT; +// } + +// if (!Array.isArray(sources)) { +// return DRAG_OVER_REJECT; +// } + +// if (sources.some((source) => { +// if (source instanceof NewStatPlaceholder) { +// return true; // NewStatPlaceholders can not be moved +// } + +// if (source.isRoot && target instanceof ExplorerItem && !target.isRoot) { +// return true; // Root folder can not be moved to a non root file stat. +// } + +// if (source.resource.toString() === target.resource.toString()) { +// return true; // Can not move anything onto itself +// } + +// if (source.isRoot && target instanceof ExplorerItem && target.isRoot) { +// // Disable moving workspace roots in one another +// return false; +// } + +// if (!isCopy && resources.dirname(source.resource).toString() === target.resource.toString()) { +// return true; // Can not move a file to the same parent unless we copy +// } + +// if (resources.isEqualOrParent(target.resource, source.resource, !isLinux /* ignorecase */)) { +// return true; // Can not move a parent folder into one of its children +// } + +// return false; +// })) { +// return DRAG_OVER_REJECT; +// } +// } + +// // All (target = model) +// if (target instanceof Model) { +// return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace +// } + +// // All (target = file/folder) +// else { +// if (target.isDirectory) { +// if (target.isReadonly) { +// return DRAG_OVER_REJECT; +// } +// return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); +// } + +// if (this.contextService.getWorkspace().folders.every(folder => folder.uri.toString() !== target.resource.toString())) { +// return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; +// } +// } + +// return DRAG_OVER_REJECT; +// } + +// public drop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): void { + +// // Desktop DND (Import file) +// if (data instanceof DesktopDragAndDropData) { +// this.handleExternalDrop(tree, data, target, originalEvent); +// } + +// // In-Explorer DND (Move/Copy file) +// else { +// this.handleExplorerDrop(tree, data, target, originalEvent); +// } +// } + +// private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { +// const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); + +// // Check for dropped external files to be folders +// return this.fileService.resolveFiles(droppedResources).then(result => { + +// // Pass focus to window +// this.windowService.focusWindow(); + +// // Handle folders by adding to workspace if we are in workspace context +// const folders = result.filter(r => r.success && r.stat.isDirectory).map(result => ({ uri: result.stat.resource })); +// if (folders.length > 0) { + +// // If we are in no-workspace context, ask for confirmation to create a workspace +// let confirmedPromise: TPromise = Promise.resolve({ confirmed: true }); +// if (this.contextService.getWorkbenchState() !== WorkbenchState.WORKSPACE) { +// confirmedPromise = this.dialogService.confirm({ +// message: folders.length > 1 ? nls.localize('dropFolders', "Do you want to add the folders to the workspace?") : nls.localize('dropFolder', "Do you want to add the folder to the workspace?"), +// type: 'question', +// primaryButton: folders.length > 1 ? nls.localize('addFolders', "&&Add Folders") : nls.localize('addFolder', "&&Add Folder") +// }); +// } + +// return confirmedPromise.then(res => { +// if (res.confirmed) { +// return this.workspaceEditingService.addFolders(folders); +// } + +// return void 0; +// }); +// } + +// // Handle dropped files (only support FileStat as target) +// else if (target instanceof ExplorerItem && !target.isReadonly) { +// const addFilesAction = this.instantiationService.createInstance(AddFilesAction, tree, target, null); + +// return addFilesAction.run(droppedResources.map(res => res.resource)); +// } + +// return void 0; +// }); +// } + +// private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { +// const sources: ExplorerItem[] = resources.distinctParents(data.getData(), s => s.resource); +// const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); + +// let confirmPromise: TPromise; + +// // Handle confirm setting +// const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); +// if (confirmDragAndDrop) { +// confirmPromise = this.dialogService.confirm({ +// message: sources.length > 1 && sources.every(s => s.isRoot) ? nls.localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") +// : sources.length > 1 ? getConfirmMessage(nls.localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", sources.length), sources.map(s => s.resource)) +// : sources[0].isRoot ? nls.localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", sources[0].name) +// : nls.localize('confirmMove', "Are you sure you want to move '{0}'?", sources[0].name), +// checkbox: { +// label: nls.localize('doNotAskAgain', "Do not ask me again") +// }, +// type: 'question', +// primaryButton: nls.localize({ key: 'moveButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Move") +// }); +// } else { +// confirmPromise = Promise.resolve({ confirmed: true } as IConfirmationResult); +// } + +// return confirmPromise.then(res => { + +// // Check for confirmation checkbox +// let updateConfirmSettingsPromise: TPromise = Promise.resolve(void 0); +// if (res.confirmed && res.checkboxChecked === true) { +// updateConfirmSettingsPromise = this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); +// } + +// return updateConfirmSettingsPromise.then(() => { +// if (res.confirmed) { +// const rootDropPromise = this.doHandleRootDrop(sources.filter(s => s.isRoot), target); +// return Promise.all(sources.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(tree, source, target, isCopy)).concat(rootDropPromise)).then(() => void 0); +// } + +// return Promise.resolve(void 0); +// }); +// }); +// } + +// private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem | Model): TPromise { +// if (roots.length === 0) { +// return Promise.resolve(undefined); +// } + +// const folders = this.contextService.getWorkspace().folders; +// let targetIndex: number; +// const workspaceCreationData: IWorkspaceFolderCreationData[] = []; +// const rootsToMove: IWorkspaceFolderCreationData[] = []; + +// for (let index = 0; index < folders.length; index++) { +// const data = { +// uri: folders[index].uri +// }; +// if (target instanceof ExplorerItem && folders[index].uri.toString() === target.resource.toString()) { +// targetIndex = workspaceCreationData.length; +// } + +// if (roots.every(r => r.resource.toString() !== folders[index].uri.toString())) { +// workspaceCreationData.push(data); +// } else { +// rootsToMove.push(data); +// } +// } +// if (target instanceof Model) { +// targetIndex = workspaceCreationData.length; +// } + +// workspaceCreationData.splice(targetIndex, 0, ...rootsToMove); +// return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); +// } + +// private doHandleExplorerDrop(tree: ITree, source: ExplorerItem, target: ExplorerItem | Model, isCopy: boolean): TPromise { +// if (!(target instanceof ExplorerItem)) { +// return Promise.resolve(void 0); +// } + +// return tree.expand(target).then(() => { + +// if (target.isReadonly) { +// return void 0; +// } + +// // Reuse duplicate action if user copies +// if (isCopy) { +// return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run(); +// } + +// // Otherwise move +// const targetResource = resources.joinPath(target.resource, source.name); + +// return this.textFileService.move(source.resource, targetResource).then(void 0, error => { + +// // Conflict +// if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { +// const confirm: IConfirmation = { +// message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), +// detail: nls.localize('irreversible', "This action is irreversible!"), +// primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), +// type: 'warning' +// }; + +// // Move with overwrite if the user confirms +// return this.dialogService.confirm(confirm).then(res => { +// if (res.confirmed) { +// return this.textFileService.move(source.resource, targetResource, true /* overwrite */).then(void 0, error => this.notificationService.error(error)); +// } + +// return void 0; +// }); +// } + +// // Any other error +// else { +// this.notificationService.error(error); +// } + +// return void 0; +// }); +// }, errors.onUnexpectedError); +// } +// } From 99ce253309b4ba6a21df73a2829eea65960ed061 Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Thu, 13 Dec 2018 17:45:23 -0800 Subject: [PATCH 0005/1066] 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 0006/1066] 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 a6b893c6a34199b91c010838387f07913200d1ee Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 11:06:30 +0100 Subject: [PATCH 0007/1066] explorer view height 100% --- .../parts/files/electron-browser/media/explorerviewlet.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 c3fc2504f70..fa284e26109 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -9,7 +9,8 @@ } /* --- Explorer viewlet --- */ -.explorer-viewlet { +.explorer-viewlet, +.explorer-folders-view { height: 100%; } From 3a935fa495c4842023a003526be74785c1cea29f Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 11:23:09 +0100 Subject: [PATCH 0008/1066] explorer: slight reorder and polish --- .../electron-browser/views/explorerView.ts | 195 +++++++++--------- 1 file changed, 101 insertions(+), 94 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 d6453fc9c88..d5d7ae3d792 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -55,12 +55,10 @@ export interface IExplorerViewOptions extends IViewletViewOptions { export class ExplorerView extends ViewletPanel implements IExplorerView { - public static readonly ID: string = 'workbench.explorer.fileView'; + static readonly ID: string = 'workbench.explorer.fileView'; private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes - public readonly id: string = ExplorerView.ID; - private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; private isCreated: boolean; @@ -73,14 +71,12 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private readonlyContext: IContextKey; private rootContext: IContextKey; - private fileEventsFilter: ResourceGlobMatcher; - private shouldRefresh: boolean; - private autoReveal: boolean; private sortOrder: SortOrder; private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; - private isDisposed: boolean; + private autoReveal = false; + private isDisposed = false; constructor( options: IExplorerViewOptions, @@ -102,11 +98,9 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { @IMenuService private menuService: IMenuService, @IClipboardService private clipboardService: IClipboardService ) { - super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); + super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); this._editableExplorerItems = new EditableExplorerItems(); - this.autoReveal = true; - this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); this.resourceContext = instantiationService.createInstance(ResourceContextKey); @@ -115,12 +109,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.readonlyContext = ExplorerResourceReadonlyContext.bindTo(contextKeyService); this.rootContext = ExplorerRootContext.bindTo(contextKeyService); - this.fileEventsFilter = instantiationService.createInstance( - ResourceGlobMatcher, - (root: URI) => this.getFileEventsExcludes(root), - (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) - ); - this.decorationProvider = new ExplorerDecorationsProvider(this.model, contextService); decorationService.registerDecorationsProvider(this.decorationProvider); this.disposables.push(this.decorationProvider); @@ -173,6 +161,38 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return this._editableExplorerItems; } + // Memoized locals + @memoize private get fileEventsFilter(): ResourceGlobMatcher { + const fileEventsFilter = this.instantiationService.createInstance( + ResourceGlobMatcher, + (root: URI) => this.getFileEventsExcludes(root), + (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) + ); + this.disposables.push(fileEventsFilter); + + return fileEventsFilter; + } + + @memoize private get contributedContextMenu(): IMenu { + const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService); + this.disposables.push(contributedContextMenu); + return contributedContextMenu; + } + + @memoize private get fileCopiedContextKey(): IContextKey { + return FileCopiedContext.bindTo(this.contextKeyService); + } + + @memoize + private get model(): Model { + const model = this.instantiationService.createInstance(Model); + this.disposables.push(model); + + return model; + } + + // Split view methods + render(): void { super.render(); @@ -260,36 +280,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } - private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { - if (this.isDisposed) { - return; // guard against possible race condition when config change causes recreate of views - } - - this.autoReveal = configuration && configuration.explorer && configuration.explorer.autoReveal; - - // Push down config updates to components of viewer - let needsRefresh = false; - if (this.filter) { - needsRefresh = this.filter.updateConfiguration(); - } - - const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; - if (this.sortOrder !== configSortOrder) { - this.sortOrder = configSortOrder; - needsRefresh = true; - } - - if (event && !needsRefresh) { - needsRefresh = event.affectsConfiguration('explorer.decorations.colors') - || event.affectsConfiguration('explorer.decorations.badges'); - } - - // Refresh viewer as needed if this originates from a config event - if (event && needsRefresh) { - this.doRefresh(); - } - } - focus(): void { super.focus(); @@ -365,10 +355,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } - collapseAll(): void { - this.tree.collapseAll(); - } - private openFocusedElement(preserveFocus?: boolean): void { const focusedElements = this.tree.getFocus(); const stat = focusedElements && focusedElements.length ? focusedElements[0] : undefined; @@ -389,14 +375,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return toResource(input, { supportSideBySide: true }); } - @memoize - private get model(): Model { - const model = this.instantiationService.createInstance(Model); - this.disposables.push(model); - - return model; - } - private createTree(container: HTMLElement): void { // TODO@isidor missing features // const controller = this.instantiationService.createInstance(FileController); @@ -422,6 +400,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { filter: this.filter }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + this.disposables.push(this.tree); filesRenderer.acquireTree(this.tree); // Bind context keys FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); @@ -465,6 +444,38 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return DOM.getLargestChildWidth(parentNode, childNodes); } + // React on events + + private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { + if (this.isDisposed) { + return; // guard against possible race condition when config change causes recreate of views + } + + this.autoReveal = configuration && configuration.explorer && configuration.explorer.autoReveal; + + // Push down config updates to components of viewer + let needsRefresh = false; + if (this.filter) { + needsRefresh = this.filter.updateConfiguration(); + } + + const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + if (this.sortOrder !== configSortOrder) { + this.sortOrder = configSortOrder; + needsRefresh = true; + } + + if (event && !needsRefresh) { + needsRefresh = event.affectsConfiguration('explorer.decorations.colors') + || event.affectsConfiguration('explorer.decorations.badges'); + } + + // Refresh viewer as needed if this originates from a config event + if (event && needsRefresh) { + this.doRefresh(); + } + } + private onFileOperation(e: FileOperationEvent): void { if (!this.isCreated) { return; // ignore if not yet created @@ -703,6 +714,36 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } + private onContextMenu(e: ITreeContextMenuEvent): void { + const stat = e.element; + if (stat instanceof NewStatPlaceholder) { + return; + } + + // update dynamic contexts + this.fileCopiedContextKey.set(this.clipboardService.hasResources()); + + const selection = this.tree.getSelection(); + this.contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => { + const actions: IAction[] = []; + fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); + return actions; + }, + onHide: (wasCancelled?: boolean) => { + if (wasCancelled) { + this.tree.domFocus(); + } + }, + getActionsContext: () => selection && selection.indexOf(stat) >= 0 + ? selection.map((fs: ExplorerItem) => fs.resource) + : stat instanceof ExplorerItem ? [stat.resource] : [] + }); + } + + // General methods + /** * Refresh the contents of the explorer to get up to date data from the disk about the file structure. */ @@ -932,42 +973,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } - private onContextMenu(e: ITreeContextMenuEvent): void { - const stat = e.element; - if (stat instanceof NewStatPlaceholder) { - return; - } - - // update dynamic contexts - this.fileCopiedContextKey.set(this.clipboardService.hasResources()); - - const selection = this.tree.getSelection(); - this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => { - const actions: IAction[] = []; - fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); - return actions; - }, - onHide: (wasCancelled?: boolean) => { - if (wasCancelled) { - this.tree.domFocus(); - } - }, - getActionsContext: () => selection && selection.indexOf(stat) >= 0 - ? selection.map((fs: ExplorerItem) => fs.resource) - : stat instanceof ExplorerItem ? [stat.resource] : [] - }); - } - - @memoize private get contributedContextMenu(): IMenu { - const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService); - this.disposables.push(contributedContextMenu); - return contributedContextMenu; - } - - @memoize private get fileCopiedContextKey(): IContextKey { - return FileCopiedContext.bindTo(this.contextKeyService); + collapseAll(): void { + this.tree.collapseAll(); } dispose(): void { From 4a8758b60ba639e93dccaecb7eb422a13ff81798 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 11:33:45 +0100 Subject: [PATCH 0009/1066] explorer: memoize actions and some polish --- .../electron-browser/views/explorerView.ts | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 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 d5d7ae3d792..5c645cd535a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -53,6 +53,13 @@ export interface IExplorerViewOptions extends IViewletViewOptions { fileViewletState: EditableExplorerItems; } +function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): glob.IExpression { + const scope = root ? { resource: root } : void 0; + const configuration = configurationService.getValue(scope); + + return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); +} + export class ExplorerView extends ViewletPanel implements IExplorerView { static readonly ID: string = 'workbench.explorer.fileView'; @@ -115,13 +122,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.disposables.push(this.resourceContext); } - private getFileEventsExcludes(root?: URI): glob.IExpression { - const scope = root ? { resource: root } : void 0; - const configuration = this.configurationService.getValue(scope); - - return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); - } - protected renderHeader(container: HTMLElement): void { super.renderHeader(container); @@ -165,7 +165,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { @memoize private get fileEventsFilter(): ResourceGlobMatcher { const fileEventsFilter = this.instantiationService.createInstance( ResourceGlobMatcher, - (root: URI) => this.getFileEventsExcludes(root), + (root: URI) => getFileEventsExcludes(this.configurationService, root), (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) ); this.disposables.push(fileEventsFilter); @@ -230,7 +230,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { })); } - getActions(): IAction[] { + @memoize getActions(): IAction[] { const actions: Action[] = []; actions.push(this.instantiationService.createInstance(NewFileAction, this.tree, this._editableExplorerItems, null)); @@ -290,7 +290,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (this.autoReveal) { const selection = this.tree.getSelection(); if (selection.length > 0) { - this.reveal(selection[0], 0.5); + this.tree.reveal(selection[0], 0.5); } } @@ -506,7 +506,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // Refresh the Parent (View) this.tree.refresh(p).then(() => { // Reveal and focus new element - this.reveal(childElement, 0.5); + this.tree.reveal(childElement, 0.5); this.tree.setFocus([childElement]); }); }); @@ -703,7 +703,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.explorerRefreshDelayer.trigger(() => { return this.doRefresh().then(() => { if (newRoots.length === 1) { - return this.reveal(this.model.findClosest(newRoots[0].uri), 0.5); + return this.tree.reveal(this.model.findClosest(newRoots[0].uri), 0.5); } return undefined; @@ -900,7 +900,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { const selection = this.hasSingleSelection(resource); if (selection) { if (reveal) { - this.reveal(selection, 0.5); + this.tree.reveal(selection, 0.5); } return; @@ -957,7 +957,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // } if (reveal) { - this.reveal(fileStat, 0.5); + this.tree.reveal(fileStat, 0.5); } if (!fileStat.isDirectory) { @@ -967,12 +967,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.tree.setFocus([fileStat]); } - private reveal(element: any, relativeTop?: number): void { - if (this.tree) { - this.tree.reveal(element, relativeTop); - } - } - collapseAll(): void { this.tree.collapseAll(); } From 05cf208347c122a320765b83602dca23f893824a Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 11:36:32 +0100 Subject: [PATCH 0010/1066] explorer: do not memoize actions, that was too optimisitc --- .../parts/files/electron-browser/views/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 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 5c645cd535a..161f0bded74 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -230,7 +230,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { })); } - @memoize getActions(): IAction[] { + getActions(): IAction[] { const actions: Action[] = []; actions.push(this.instantiationService.createInstance(NewFileAction, this.tree, this._editableExplorerItems, null)); From 5ee32f1796ebb3c6d18f64a291c687dac9397628 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 11:58:55 +0100 Subject: [PATCH 0011/1066] explorer: when selecting a stat make sure to expand all elements in the parent chain --- .../electron-browser/views/explorerView.ts | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 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 161f0bded74..017ab546f53 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { ThrottledDelayer } from 'vs/base/common/async'; +import { ThrottledDelayer, sequence, ignoreErrors } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import * as glob from 'vs/base/common/glob'; @@ -906,7 +906,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return; } - // First try to get the stat object from the input to avoid a roundtrip if (!this.isCreated) { return; } @@ -942,29 +941,30 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { : undefined; } - private doSelect(fileStat: ExplorerItem, reveal: boolean): void { + private doSelect(fileStat: ExplorerItem, reveal: boolean): Promise { if (!fileStat) { - return; + return Promise.resolve(void 0); } - // // Special case: we are asked to reveal and select an element that is not visible - // // In this case we take the parent element so that we are at least close to it. - // if (!this.tree.isVisible(fileStat)) { - // fileStat = fileStat.parent; - // if (!fileStat) { - // return; - // } - // } - - if (reveal) { - this.tree.reveal(fileStat, 0.5); + // Expand all stats in the parent chain + const toExpand: ExplorerItem[] = []; + let parent = fileStat.parent; + while (parent) { + toExpand.push(parent); + parent = parent.parent; } - if (!fileStat.isDirectory) { - this.tree.setSelection([fileStat]); // Since folders can not be opened, only select files - } + return sequence(toExpand.reverse().map(s => () => ignoreErrors(this.tree.expand(s)))).then(() => { + if (reveal) { + this.tree.reveal(fileStat, 0.5); + } - this.tree.setFocus([fileStat]); + if (!fileStat.isDirectory) { + this.tree.setSelection([fileStat]); // Since folders can not be opened, only select files + } + + this.tree.setFocus([fileStat]); + }); } collapseAll(): void { From ed752d077247e2e4b9246fa72dcfb1addcfb7239 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 12:27:38 +0100 Subject: [PATCH 0012/1066] explorer: properly open elements on user selection --- .../electron-browser/views/explorerView.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 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 017ab546f53..2e53f8259fc 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -38,7 +38,7 @@ import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/l import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, EditableExplorerItems as EditableExplorerItems, FilesFilter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; @@ -183,8 +183,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return FileCopiedContext.bindTo(this.contextKeyService); } - @memoize - private get model(): Model { + @memoize private get model(): Model { const model = this.instantiationService.createInstance(Model); this.disposables.push(model); @@ -424,12 +423,18 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // Open when selecting via keyboard this.disposables.push(this.tree.onDidChangeSelection(e => { const selection = e.elements; - if (selection && selection.length) { - // TODO@Isidor check this, and add side by side, focus passing - const stat = selection[0]; + // Do not react if the user is expanding selection + if (selection && selection.length === 1) { + let isDoubleClick = false; + let sideBySide = false; + if (e.browserEvent instanceof MouseEvent) { + isDoubleClick = e.browserEvent.detail === 2; + sideBySide = this.tree.useAltAsMultipleSelectionModifier ? (e.browserEvent.ctrlKey || e.browserEvent.metaKey) : e.browserEvent.altKey; + } - if (!stat.isDirectory) { - this.editorService.openEditor({ resource: stat.resource }); + if (!selection[0].isDirectory) { + // Pass focus for keyboard events and for double click + this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: !isDoubleClick, pinned: isDoubleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } })); From ab0b87329f1e5c89f1df6e50c5a4b7e7a37006dc Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 16:58:14 +0100 Subject: [PATCH 0013/1066] explorer: send telemetry when file is opened --- .../files/electron-browser/views/explorerView.ts | 12 ++++++++++-- 1 file changed, 10 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 2e53f8259fc..daa3fc21e2a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -48,6 +48,7 @@ import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export interface IExplorerViewOptions extends IViewletViewOptions { fileViewletState: EditableExplorerItems; @@ -103,7 +104,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { @IThemeService private themeService: IWorkbenchThemeService, @IListService private listService: IListService, @IMenuService private menuService: IMenuService, - @IClipboardService private clipboardService: IClipboardService + @IClipboardService private clipboardService: IClipboardService, + @ITelemetryService private telemetryService: ITelemetryService ) { super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); @@ -434,6 +436,12 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (!selection[0].isDirectory) { // Pass focus for keyboard events and for double click + /* __GDPR__ + "workbenchActionExecuted" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + }*/ + this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: !isDoubleClick, pinned: isDoubleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } @@ -541,7 +549,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { const modelElements = this.model.findAll(oldResource); modelElements.forEach(modelElement => { //Check if element is expanded - isExpanded = this.tree.isExpanded(modelElement); + isExpanded = !this.tree.isCollapsed(modelElement); // Rename File (Model) modelElement.rename(newElement); From b25d7e2b267ba25e1ed84e8880048b2aac29d665 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 14 Dec 2018 17:29:44 +0100 Subject: [PATCH 0014/1066] explorer: some notes and clenaup --- .../files/electron-browser/views/explorerView.ts | 7 ------- .../files/electron-browser/views/explorerViewer.ts | 12 +++--------- 2 files changed, 3 insertions(+), 16 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 daa3fc21e2a..51dbf05de65 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -377,12 +377,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } private createTree(container: HTMLElement): void { - // TODO@isidor missing features - // const controller = this.instantiationService.createInstance(FileController); - // this.disposables.push(controller); - // const sorter = this.instantiationService.createInstance(FileSorter); - // this.disposables.push(sorter); - // const dnd = this.instantiationService.createInstance(FileDragAndDrop); this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); const filesRenderer = this.instantiationService.createInstance(FilesRenderer, this._editableExplorerItems); @@ -402,7 +396,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.disposables.push(this.tree); - filesRenderer.acquireTree(this.tree); // Bind context keys FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); ExplorerFocusedContext.bindTo(this.tree.contextKeyService); 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 c0726ee7efd..d5e9db666ed 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -32,7 +32,6 @@ import { once } from 'vs/base/common/functional'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { normalize, join, nativeSep } from 'vs/base/common/paths'; import { rtrim } from 'vs/base/common/strings'; -import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; @@ -147,7 +146,6 @@ export class FilesRenderer implements ITreeRenderer; constructor( state: EditableExplorerItems, @@ -171,10 +169,6 @@ export class FilesRenderer implements ITreeRenderer): void { - this.tree = tree; - } - renderTemplate(container: HTMLElement): IFileTemplateData { const elementDisposable = Disposable.None; const label = this.instantiationService.createInstance(FileLabel, container, void 0); @@ -244,18 +238,18 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); inputBox.focus(); - const done = once((commit: boolean, blur: boolean) => { + const done = once(async (commit: boolean, blur: boolean) => { label.element.style.display = 'none'; if (stat instanceof NewStatPlaceholder) { stat.destroy(); } if (commit && inputBox.value) { - editableData.action.run({ value: inputBox.value }); + await editableData.action.run({ value: inputBox.value }); } dispose(toDispose); container.removeChild(label.element); - this.tree.refresh(stat.parent).then(() => this.tree.domFocus()); + // todo@isidor need to unset editable data }); const toDispose = [ From c14ac109a2fc8e6bd51deaac246ed2018ee8e0ce Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 13 Dec 2018 22:35:06 -0800 Subject: [PATCH 0015/1066] 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 0016/1066] 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 0017/1066] 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 0018/1066] 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 39b480b67ec8921cbb3db91561c41e3cfc92ec72 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Dec 2018 11:04:51 +0100 Subject: [PATCH 0019/1066] fix compile error, IDataSource moved to another file --- .../parts/files/electron-browser/views/explorerViewer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 d5e9db666ed..08bbb579731 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -8,7 +8,6 @@ import * as DOM from 'vs/base/browser/dom'; import * as glob from 'vs/base/common/glob'; import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { IDataSource } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IFileService, FileKind } from 'vs/platform/files/common/files'; @@ -17,7 +16,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IDataSource } from 'vs/base/browser/ui/tree/tree'; import { IFileViewletState, IEditableData } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; From 8899a0cb34556528b0ebe1c8829081610a74a7ae Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Dec 2018 15:45:31 +0100 Subject: [PATCH 0020/1066] explorer: introduce ExplorerService --- src/vs/workbench/parts/files/browser/files.ts | 3 +- .../{explorerModel.ts => explorerService.ts} | 57 +++--------- src/vs/workbench/parts/files/common/files.ts | 87 +++++++++++++------ .../files/electron-browser/fileActions.ts | 12 +-- .../electron-browser/files.contribution.ts | 6 +- .../views/explorerDecorationsProvider.ts | 6 +- .../electron-browser/views/explorerView.ts | 54 +++++------- .../electron-browser/views/explorerViewer.ts | 13 +-- .../electron-browser/views/openEditorsView.ts | 3 +- ...rModel.test.ts => explorerService.test.ts} | 2 +- 10 files changed, 119 insertions(+), 124 deletions(-) rename src/vs/workbench/parts/files/common/{explorerModel.ts => explorerService.ts} (91%) rename src/vs/workbench/parts/files/test/electron-browser/{explorerModel.test.ts => explorerService.test.ts} (99%) diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index 00f7804d9df..ac5b43d9cca 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -5,7 +5,8 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; -import { ExplorerItem, OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; +import { OpenEditor } from 'vs/workbench/parts/files/common/files'; import { toResource } from 'vs/workbench/common/editor'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { List } from 'vs/base/browser/ui/list/listWidget'; diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerService.ts similarity index 91% rename from src/vs/workbench/parts/files/common/explorerModel.ts rename to src/vs/workbench/parts/files/common/explorerService.ts index 4f101f1d1b4..0c9301293fd 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerService.ts @@ -4,20 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { ResourceMap } from 'vs/base/common/map'; import { isLinux } from 'vs/base/common/platform'; import { IFileStat } from 'vs/platform/files/common/files'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { toResource, IEditorIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings'; -import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { coalesce } from 'vs/base/common/arrays'; +import { IExplorerService } from 'vs/workbench/parts/files/common/files'; -export class Model { +export class ExplorerService implements IExplorerService { + _serviceBrand: any; + + setEditable(stat: ExplorerItem, editable: boolean): void { + throw new Error('Method not implemented.'); + } + + onDidEditStat: Event; private _roots: ExplorerItem[]; private _listener: IDisposable; @@ -450,46 +456,3 @@ export class NewStatPlaceholder extends ExplorerItem { return child; } } - -export class OpenEditor implements IEditorIdentifier { - - constructor(private _editor: IEditorInput, private _group: IEditorGroup) { - // noop - } - - public get editor() { - return this._editor; - } - - public get editorIndex() { - return this._group.getIndexOfEditor(this.editor); - } - - public get group() { - return this._group; - } - - public get groupId() { - return this._group.id; - } - - public getId(): string { - return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`; - } - - public isPreview(): boolean { - return this._group.previewEditor === this.editor; - } - - public isUntitled(): boolean { - return !!toResource(this.editor, { supportSideBySide: true, filter: Schemas.untitled }); - } - - public isDirty(): boolean { - return this.editor.isDirty(); - } - - public getResource(): URI | null { - return toResource(this.editor, { supportSideBySide: true }); - } -} diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index a5e27003514..4ef69ff73e3 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -5,13 +5,13 @@ import { URI } from 'vs/base/common/uri'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; +import { IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorInput, toResource } from 'vs/workbench/common/editor'; import { IFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files'; -import { ExplorerItem, OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; +import { Event } from 'vs/base/common/event'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -20,6 +20,9 @@ import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys import { Registry } from 'vs/platform/registry/common/platform'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; import { Schemas } from 'vs/base/common/network'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; +import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; /** * Explorer viewlet id. @@ -38,6 +41,17 @@ export interface IExplorerView { select(resource: URI, reveal?: boolean): void; } +export interface IExplorerService { + _serviceBrand: any; + readonly roots: ExplorerItem[]; + readonly onDidEditStat: Event; + + setEditable(stat: ExplorerItem, editable: boolean): void; + findClosest(resource: URI): ExplorerItem | null; + findAll(resource: URI): ExplorerItem[]; +} +export const IExplorerService = createDecorator('explorerService'); + /** * Context Keys to use with keybindings for the Explorer and Open Editors view */ @@ -107,32 +121,6 @@ export interface IFileResource { isDirectory?: boolean; } -/** - * Helper to get an explorer item from an object. - */ -export function explorerItemToFileResource(obj: ExplorerItem | OpenEditor): IFileResource | null { - if (obj instanceof ExplorerItem) { - const stat = obj as ExplorerItem; - - return { - resource: stat.resource, - isDirectory: stat.isDirectory - }; - } - - if (obj instanceof OpenEditor) { - const editor = obj as OpenEditor; - const resource = editor.getResource(); - if (resource) { - return { - resource - }; - } - } - - return null; -} - export const SortOrderConfiguration = { DEFAULT: 'default', MIXED: 'mixed', @@ -210,3 +198,46 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider { this.fileWatcher = dispose(this.fileWatcher); } } + +export class OpenEditor implements IEditorIdentifier { + + constructor(private _editor: IEditorInput, private _group: IEditorGroup) { + // noop + } + + public get editor() { + return this._editor; + } + + public get editorIndex() { + return this._group.getIndexOfEditor(this.editor); + } + + public get group() { + return this._group; + } + + public get groupId() { + return this._group.id; + } + + public getId(): string { + return `openeditor:${this.groupId}:${this.editorIndex}:${this.editor.getName()}:${this.editor.getDescription()}`; + } + + public isPreview(): boolean { + return this._group.previewEditor === this.editor; + } + + public isUntitled(): boolean { + return !!toResource(this.editor, { supportSideBySide: true, filter: Schemas.untitled }); + } + + public isDirty(): boolean { + return this.editor.isDirty(); + } + + public getResource(): URI | null { + return toResource(this.editor, { supportSideBySide: true }); + } +} diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index a84928487e4..a9e107c0ab0 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -21,7 +21,6 @@ import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService, IFileStat, AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; -import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; @@ -49,6 +48,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { EditableExplorerItems } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { NewStatPlaceholder, ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; export interface IEditableData { action: IAction; @@ -767,8 +767,9 @@ export class AddFilesAction extends BaseFileAction { if (this.element) { targetElement = this.element; } else { - const input: ExplorerItem | Model = this.tree.getInput(); - targetElement = this.tree.getFocus() || (input instanceof Model ? input.roots[0] : input); + // TODO@isidor + // const input: ExplorerItem | Model = this.tree.getInput(); + // targetElement = this.tree.getFocus() || (input instanceof Model ? input.roots[0] : input); } if (!targetElement.isDirectory) { @@ -903,8 +904,9 @@ class PasteFileAction extends BaseFileAction { this.tree = tree; this.element = element; if (!this.element) { - const input: ExplorerItem | Model = this.tree.getInput(); - this.element = input instanceof Model ? input.roots[0] : input; + // TODO@isidor + // const input: ExplorerItem | Model = this.tree.getInput(); + // this.element = input instanceof Model ? input.roots[0] : input; } this._updateEnablement(); } diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index f857f474d90..451b36ca422 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -13,7 +13,7 @@ import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/wor import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; -import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID } from 'vs/workbench/parts/files/common/files'; +import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { FileEditorTracker } from 'vs/workbench/parts/files/browser/editors/fileEditorTracker'; import { SaveErrorHandler } from 'vs/workbench/parts/files/electron-browser/saveErrorHandler'; import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; @@ -35,6 +35,8 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import { ILabelService } from 'vs/platform/label/common/label'; import { nativeSep } from 'vs/base/common/paths'; import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ExplorerService } from 'vs/workbench/parts/files/common/explorerService'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { @@ -79,6 +81,8 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie 0 )); +registerSingleton(IExplorerService, ExplorerService); + Registry.as(ViewletExtensions.Viewlets).setDefaultViewletId(VIEWLET_ID); const openViewletKb: IKeybindings = { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts index 2fcb0fa5e2a..e4fc759afaa 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts @@ -6,12 +6,12 @@ import { URI } from 'vs/base/common/uri'; import { Event, Emitter } from 'vs/base/common/event'; import { localize } from 'vs/nls'; -import { Model } from 'vs/workbench/parts/files/common/explorerModel'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/decorations/browser/decorations'; import { listInvalidItemForeground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vscode-xterm'; import { dispose } from 'vs/base/common/lifecycle'; +import { IExplorerService } from 'vs/workbench/parts/files/common/files'; export class ExplorerDecorationsProvider implements IDecorationsProvider { readonly label: string = localize('label', "Explorer"); @@ -19,7 +19,7 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider { private toDispose: IDisposable[]; constructor( - private model: Model, + @IExplorerService private explorerService: IExplorerService, @IWorkspaceContextService contextService: IWorkspaceContextService ) { this.toDispose = []; @@ -37,7 +37,7 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider { } provideDecorations(resource: URI): IDecorationData | undefined { - const fileStat = this.model.findClosest(resource); + const fileStat = this.explorerService.findClosest(resource); if (fileStat && fileStat.isRoot && fileStat.isError) { return { tooltip: localize('canNotResolve', "Can not resolve workspace folder"), 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 51dbf05de65..045a3083d6b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -12,7 +12,7 @@ import * as resources from 'vs/base/common/resources'; import * as glob from 'vs/base/common/glob'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext } from 'vs/workbench/parts/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService, FILES_EXCLUDE_CONFIG, IFileStat } from 'vs/platform/files/common/files'; import { RefreshViewExplorerAction, NewFolderAction, NewFileAction, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { toResource } from 'vs/workbench/common/editor'; @@ -20,7 +20,6 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as DOM from 'vs/base/browser/dom'; import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ExplorerDecorationsProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -49,6 +48,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; export interface IExplorerViewOptions extends IViewletViewOptions { fileViewletState: EditableExplorerItems; @@ -105,7 +105,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { @IListService private listService: IListService, @IMenuService private menuService: IMenuService, @IClipboardService private clipboardService: IClipboardService, - @ITelemetryService private telemetryService: ITelemetryService + @ITelemetryService private telemetryService: ITelemetryService, + @IExplorerService private explorerService: IExplorerService ) { super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); @@ -118,7 +119,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.readonlyContext = ExplorerResourceReadonlyContext.bindTo(contextKeyService); this.rootContext = ExplorerRootContext.bindTo(contextKeyService); - this.decorationProvider = new ExplorerDecorationsProvider(this.model, contextService); + this.decorationProvider = new ExplorerDecorationsProvider(this.explorerService, contextService); decorationService.registerDecorationsProvider(this.decorationProvider); this.disposables.push(this.decorationProvider); this.disposables.push(this.resourceContext); @@ -185,13 +186,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return FileCopiedContext.bindTo(this.contextKeyService); } - @memoize private get model(): Model { - const model = this.instantiationService.createInstance(Model); - this.disposables.push(model); - - return model; - } - // Split view methods render(): void { @@ -383,7 +377,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.disposables.push(filesRenderer); this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer], - this.instantiationService.createInstance(ExplorerDataSource, this.model), { + this.instantiationService.createInstance(ExplorerDataSource), { accessibilityProvider: new ExplorerAccessibilityProvider(), ariaLabel: nls.localize('treeAriaLabel', "Files Explorer"), identityProvider: { @@ -491,7 +485,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { const addedElement = e.target; const parentResource = resources.dirname(addedElement.resource); - const parents = this.model.findAll(parentResource); + const parents = this.explorerService.findAll(parentResource); if (parents.length) { @@ -539,7 +533,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { let isExpanded = false; // Handle Rename if (oldParentResource && newParentResource && oldParentResource.toString() === newParentResource.toString()) { - const modelElements = this.model.findAll(oldResource); + const modelElements = this.explorerService.findAll(oldResource); modelElements.forEach(modelElement => { //Check if element is expanded isExpanded = !this.tree.isCollapsed(modelElement); @@ -563,8 +557,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // Handle Move else if (oldParentResource && newParentResource) { - const newParents = this.model.findAll(newParentResource); - const modelElements = this.model.findAll(oldResource); + const newParents = this.explorerService.findAll(newParentResource); + const modelElements = this.explorerService.findAll(oldResource); if (newParents.length && modelElements.length) { @@ -585,7 +579,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // Delete else if (e.operation === FileOperation.DELETE) { - const modelElements = this.model.findAll(e.resource); + const modelElements = this.explorerService.findAll(e.resource); modelElements.forEach(element => { if (element.parent) { const parent = element.parent; @@ -643,8 +637,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } // Compute if parent is visible and added file not yet part of it - const parentStat = this.model.findClosest(parent); - if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { + const parentStat = this.explorerService.findClosest(parent); + if (parentStat && parentStat.isDirectoryResolved && !this.explorerService.findClosest(change.resource)) { return true; } @@ -663,7 +657,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { for (let j = 0; j < deleted.length; j++) { const del = deleted[j]; - if (this.model.findClosest(del.resource)) { + if (this.explorerService.findClosest(del.resource)) { return true; } } @@ -677,7 +671,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { for (let j = 0; j < updated.length; j++) { const upd = updated[j]; - if (this.model.findClosest(upd.resource)) { + if (this.explorerService.findClosest(upd.resource)) { return true; } } @@ -709,7 +703,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this.explorerRefreshDelayer.trigger(() => { return this.doRefresh().then(() => { if (newRoots.length === 1) { - return this.tree.reveal(this.model.findClosest(newRoots[0].uri), 0.5); + return this.tree.reveal(this.explorerService.findClosest(newRoots[0].uri), 0.5); } return undefined; @@ -783,7 +777,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } private doRefresh(): Promise { - const targetsToResolve = this.model.roots.map(root => ({ root, resource: root.resource, options: { resolveTo: [] } })); + const targetsToResolve = this.explorerService.roots.map(root => ({ root, resource: root.resource, options: { resolveTo: [] } })); // First time refresh: Receive target through active editor input or selection and also include settings from previous session if (!this.isCreated) { @@ -843,8 +837,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { }); // Subsequent refresh: Merge stat into our local model and refresh tree modelStats.forEach((modelStat, index) => { - if (index < this.model.roots.length) { - ExplorerItem.mergeLocalWithDisk(modelStat, this.model.roots[index]); + if (index < this.explorerService.roots.length) { + ExplorerItem.mergeLocalWithDisk(modelStat, this.explorerService.roots[index]); } }); @@ -857,8 +851,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { .then(result => result.isDirectory ? ExplorerItem.create(result, target.root, target.options.resolveTo) : errorRoot(target.resource, target.root), () => errorRoot(target.resource, target.root)) .then(modelStat => { // Subsequent refresh: Merge stat into our local model and refresh tree - if (index < this.model.roots.length) { - ExplorerItem.mergeLocalWithDisk(modelStat, this.model.roots[index]); + if (index < this.explorerService.roots.length) { + ExplorerItem.mergeLocalWithDisk(modelStat, this.explorerService.roots[index]); } return this.tree.refresh(null); @@ -916,7 +910,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return; } - const fileStat = this.model.findClosest(resource); + const fileStat = this.explorerService.findClosest(resource); if (fileStat) { this.doSelect(fileStat, reveal); return; @@ -925,11 +919,11 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - const rootUri = workspaceFolder ? workspaceFolder.uri : this.model.roots[0].resource; + const rootUri = workspaceFolder ? workspaceFolder.uri : this.explorerService.roots[0].resource; this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model - const root = this.model.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + const root = this.explorerService.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); const modelStat = ExplorerItem.create(stat, root, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); 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 08bbb579731..3a6fb501de0 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -6,7 +6,6 @@ import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import * as DOM from 'vs/base/browser/dom'; import * as glob from 'vs/base/common/glob'; -import { ExplorerItem, Model, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -22,7 +21,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration } from 'vs/workbench/parts/files/common/files'; +import { IFilesConfiguration, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { dirname, joinPath, basename } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; @@ -33,6 +32,7 @@ import { normalize, join, nativeSep } from 'vs/base/common/paths'; import { rtrim } from 'vs/base/common/strings'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; +import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -50,7 +50,7 @@ export class ExplorerDelegate implements IListVirtualDelegate { export class ExplorerDataSource implements IDataSource { constructor( - private model: Model, + @IExplorerService private explorerService: IExplorerService, @IProgressService private progressService: IProgressService, @INotificationService private notificationService: INotificationService, @IFileService private fileService: IFileService, @@ -64,11 +64,12 @@ export class ExplorerDataSource implements IDataSource { getChildren(element: ExplorerItem | null): Promise { if (element === null) { - if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || this.model.roots[0].isError) { + const roots = this.explorerService.roots; + if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || roots[0].isError) { // Display roots only when multi folder workspace - return Promise.resolve(this.model.roots); + return Promise.resolve(roots); } - element = this.model.roots[0]; + element = roots[0]; } // Return early if stat is already resolved 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 400f005c7d4..983c13fedd7 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -14,9 +14,8 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorInput } from 'vs/workbench/common/editor'; import { SaveAllAction, SaveAllInGroupAction, CloseGroupAction } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { OpenEditorsFocusedContext, ExplorerFocusedContext, IFilesConfiguration } from 'vs/workbench/parts/files/common/files'; +import { OpenEditorsFocusedContext, ExplorerFocusedContext, IFilesConfiguration, OpenEditor } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, AutoSaveMode } from 'vs/workbench/services/textfile/common/textfiles'; -import { OpenEditor } from 'vs/workbench/parts/files/common/explorerModel'; 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'; diff --git a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts b/src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts similarity index 99% rename from src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts rename to src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts index b4133dfedd2..3b03d9a38b2 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts @@ -9,7 +9,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/paths'; import { validateFileName } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem { return new ExplorerItem(toResource(path), undefined, false, false, isFolder, name, mtime); From 7e8905b834a64a490f1a01dc9e8edd7cd3ad922a Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Dec 2018 15:53:12 +0100 Subject: [PATCH 0021/1066] explorer service: do not use public as keyword --- .../parts/files/common/explorerService.ts | 82 ++++++++++--------- src/vs/workbench/parts/files/common/files.ts | 2 +- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerService.ts b/src/vs/workbench/parts/files/common/explorerService.ts index 0c9301293fd..bd079b58e14 100644 --- a/src/vs/workbench/parts/files/common/explorerService.ts +++ b/src/vs/workbench/parts/files/common/explorerService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { Event } from 'vs/base/common/event'; +import { Event, Emitter } from 'vs/base/common/event'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { ResourceMap } from 'vs/base/common/map'; @@ -19,13 +19,8 @@ import { IExplorerService } from 'vs/workbench/parts/files/common/files'; export class ExplorerService implements IExplorerService { _serviceBrand: any; - setEditable(stat: ExplorerItem, editable: boolean): void { - throw new Error('Method not implemented.'); - } - - onDidEditStat: Event; - private _roots: ExplorerItem[]; + private _onDidChangeEditable = new Emitter(); private _listener: IDisposable; constructor(@IWorkspaceContextService private contextService: IWorkspaceContextService) { @@ -35,16 +30,25 @@ export class ExplorerService implements IExplorerService { setRoots(); } - public get roots(): ExplorerItem[] { + get roots(): ExplorerItem[] { return this._roots; } + get onDidChangeEditable(): Event { + return this._onDidChangeEditable.event; + } + + setEditable(stat: ExplorerItem, editable: boolean): void { + this._onDidChangeEditable.fire(stat); + // TODO@isidor + } + /** * Returns an array of child stat from this stat that matches with the provided path. * Starts matching from the first root. * Will return empty array in case the FileStat does not exist. */ - public findAll(resource: URI): ExplorerItem[] { + findAll(resource: URI): ExplorerItem[] { return coalesce(this.roots.map(root => root.find(resource))); } @@ -53,7 +57,7 @@ export class ExplorerService implements IExplorerService { * In case multiple FileStat are matching the resource (same folder opened multiple times) returns the FileStat that has the closest root. * Will return null in case the FileStat does not exist. */ - public findClosest(resource: URI): ExplorerItem | null { + findClosest(resource: URI): ExplorerItem | null { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { const root = this.roots.filter(r => r.resource.toString() === folder.uri.toString()).pop(); @@ -65,7 +69,7 @@ export class ExplorerService implements IExplorerService { return null; } - public dispose(): void { + dispose(): void { this._listener = dispose(this._listener); } } @@ -101,23 +105,23 @@ export class ExplorerItem { this.isDirectoryResolved = false; } - public get isSymbolicLink(): boolean { + get isSymbolicLink(): boolean { return this._isSymbolicLink; } - public get isDirectory(): boolean { + get isDirectory(): boolean { return this._isDirectory; } - public get isReadonly(): boolean { + get isReadonly(): boolean { return this._isReadonly; } - public get isError(): boolean { + get isError(): boolean { return this._isError; } - public set isDirectory(value: boolean) { + set isDirectory(value: boolean) { if (value !== this._isDirectory) { this._isDirectory = value; if (this._isDirectory) { @@ -129,7 +133,7 @@ export class ExplorerItem { } - public get name(): string { + get name(): string { return this._name; } @@ -144,15 +148,15 @@ export class ExplorerItem { } } - public getId(): string { + getId(): string { return this.resource.toString(); } - public get isRoot(): boolean { + get isRoot(): boolean { return this === this.root; } - public static create(raw: IFileStat, root: ExplorerItem, resolveTo?: URI[], isError = false): ExplorerItem { + static create(raw: IFileStat, root: ExplorerItem, resolveTo?: URI[], isError = false): ExplorerItem { const stat = new ExplorerItem(raw.resource, root, raw.isSymbolicLink, raw.isReadonly, raw.isDirectory, raw.name, raw.mtime, raw.etag, isError); // Recursively add children if present @@ -183,7 +187,7 @@ export class ExplorerItem { * and children. The merge will only consider resolved stat elements to avoid overwriting data which * exists locally. */ - public static mergeLocalWithDisk(disk: ExplorerItem, local: ExplorerItem): void { + static mergeLocalWithDisk(disk: ExplorerItem, local: ExplorerItem): void { if (disk.resource.toString() !== local.resource.toString()) { return; // Merging only supported for stats with the same resource } @@ -242,7 +246,7 @@ export class ExplorerItem { /** * Adds a child element to this folder. */ - public addChild(child: ExplorerItem): void { + addChild(child: ExplorerItem): void { if (!this.children) { this.isDirectory = true; } @@ -256,7 +260,7 @@ export class ExplorerItem { } } - public getChild(name: string): ExplorerItem | undefined { + getChild(name: string): ExplorerItem | undefined { if (!this.children) { return undefined; } @@ -267,7 +271,7 @@ export class ExplorerItem { /** * Only use this method if you need all the children since it converts a map to an array */ - public getChildrenArray(): ExplorerItem[] | undefined { + getChildrenArray(): ExplorerItem[] | undefined { if (!this.children) { return undefined; } @@ -280,7 +284,7 @@ export class ExplorerItem { return items; } - public getChildrenCount(): number { + getChildrenCount(): number { if (!this.children) { return 0; } @@ -291,7 +295,7 @@ export class ExplorerItem { /** * Removes a child element from this folder. */ - public removeChild(child: ExplorerItem): void { + removeChild(child: ExplorerItem): void { if (this.children) { this.children.delete(this.getPlatformAwareName(child.name)); } @@ -304,7 +308,7 @@ export class ExplorerItem { /** * Moves this element under a new parent element. */ - public move(newParent: ExplorerItem, fnBetweenStates?: (callback: () => void) => void, fnDone?: () => void): void { + move(newParent: ExplorerItem, fnBetweenStates?: (callback: () => void) => void, fnDone?: () => void): void { if (!fnBetweenStates) { fnBetweenStates = (cb: () => void) => { cb(); }; } @@ -337,7 +341,7 @@ export class ExplorerItem { * Tells this stat that it was renamed. This requires changes to all children of this stat (if any) * so that the path property can be updated properly. */ - public rename(renamedStat: { name: string, mtime?: number }): void { + rename(renamedStat: { name: string, mtime?: number }): void { // Merge a subset of Properties that can change on rename this.updateName(renamedStat.name); @@ -351,7 +355,7 @@ export class ExplorerItem { * Returns a child stat from this stat that matches with the provided path. * Will return "null" in case the child does not exist. */ - public find(resource: URI): ExplorerItem | null { + find(resource: URI): ExplorerItem | null { // Return if path found // For performance reasons try to do the comparison as fast as possible if (resource && this.resource.scheme === resource.scheme && equalsIgnoreCase(this.resource.authority, resource.authority) && @@ -396,7 +400,7 @@ export class ExplorerItem { /* A helper that can be used to show a placeholder when creating a new stat */ export class NewStatPlaceholder extends ExplorerItem { - public static NAME = ''; + static readonly NAME = ''; private static ID = 0; private id: number; @@ -410,7 +414,7 @@ export class NewStatPlaceholder extends ExplorerItem { this.directoryPlaceholder = isDirectory; } - public destroy(): void { + destroy(): void { this.parent.removeChild(this); this.isDirectoryResolved = false; @@ -418,35 +422,35 @@ export class NewStatPlaceholder extends ExplorerItem { this.mtime = void 0; } - public getId(): string { + getId(): string { return `new-stat-placeholder:${this.id}:${this.parent.resource.toString()}`; } - public isDirectoryPlaceholder(): boolean { + isDirectoryPlaceholder(): boolean { return this.directoryPlaceholder; } - public addChild() { + addChild() { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - public removeChild() { + removeChild() { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - public move() { + move() { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - public rename() { + rename() { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - public find(resource: URI): ExplorerItem | null { + find(resource: URI): ExplorerItem | null { return null; } - public static addNewStatPlaceholder(parent: ExplorerItem, isDirectory: boolean): NewStatPlaceholder { + static addNewStatPlaceholder(parent: ExplorerItem, isDirectory: boolean): NewStatPlaceholder { const child = new NewStatPlaceholder(isDirectory, parent.root); // Inherit some parent properties to child diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 4ef69ff73e3..ee08e9026a7 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -44,7 +44,7 @@ export interface IExplorerView { export interface IExplorerService { _serviceBrand: any; readonly roots: ExplorerItem[]; - readonly onDidEditStat: Event; + readonly onDidChangeEditable: Event; setEditable(stat: ExplorerItem, editable: boolean): void; findClosest(resource: URI): ExplorerItem | null; From 096ffa944a304417bb344fcdc1aac56500cabf31 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 17 Dec 2018 18:18:04 +0100 Subject: [PATCH 0022/1066] explorer: actions do not take tree and funky arguments --- .../parts/files/common/explorerService.ts | 10 +- src/vs/workbench/parts/files/common/files.ts | 4 +- .../files/electron-browser/fileActions.ts | 422 ++++++------------ .../electron-browser/views/explorerView.ts | 21 +- .../electron-browser/views/explorerViewer.ts | 48 +- 5 files changed, 172 insertions(+), 333 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerService.ts b/src/vs/workbench/parts/files/common/explorerService.ts index bd079b58e14..bc19d18e70e 100644 --- a/src/vs/workbench/parts/files/common/explorerService.ts +++ b/src/vs/workbench/parts/files/common/explorerService.ts @@ -15,6 +15,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings'; import { coalesce } from 'vs/base/common/arrays'; import { IExplorerService } from 'vs/workbench/parts/files/common/files'; +import { IAction } from 'vs/base/common/actions'; export class ExplorerService implements IExplorerService { _serviceBrand: any; @@ -22,6 +23,7 @@ export class ExplorerService implements IExplorerService { private _roots: ExplorerItem[]; private _onDidChangeEditable = new Emitter(); private _listener: IDisposable; + private editableStats = new Map string, action: IAction }>(); constructor(@IWorkspaceContextService private contextService: IWorkspaceContextService) { const setRoots = () => this._roots = this.contextService.getWorkspace().folders @@ -38,9 +40,13 @@ export class ExplorerService implements IExplorerService { return this._onDidChangeEditable.event; } - setEditable(stat: ExplorerItem, editable: boolean): void { + setEditable(stat: ExplorerItem, data: { validationMessage: (value: string) => string, action: IAction }): void { + this.editableStats.set(stat, data); this._onDidChangeEditable.fire(stat); - // TODO@isidor + } + + getEditableData(stat: ExplorerItem): { validationMessage: (value: string) => string, action: IAction } | undefined { + return this.editableStats.get(stat); } /** diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index ee08e9026a7..649d7dd737d 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -23,6 +23,7 @@ import { Schemas } from 'vs/base/common/network'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IAction } from 'vs/base/common/actions'; /** * Explorer viewlet id. @@ -46,7 +47,8 @@ export interface IExplorerService { readonly roots: ExplorerItem[]; readonly onDidChangeEditable: Event; - setEditable(stat: ExplorerItem, editable: boolean): void; + setEditable(stat: ExplorerItem, data: { validationMessage: (value: string) => string, action: IAction }): void; + getEditableData(stat: ExplorerItem): { validationMessage: (value: string) => string, action: IAction } | undefined; findClosest(resource: URI): ExplorerItem | null; findAll(resource: URI): ExplorerItem[]; } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index a9e107c0ab0..98590515c05 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -14,10 +14,9 @@ import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as strings from 'vs/base/common/strings'; import { Action, IAction } from 'vs/base/common/actions'; -import { MessageType, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; -import { ITree, IHighlightEvent } from 'vs/base/parts/tree/browser/tree'; +import { IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; +import { VIEWLET_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService, IFileStat, AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; @@ -26,7 +25,7 @@ import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explo import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IInstantiationService, ServicesAccessor, IConstructorSignature3 } from 'vs/platform/instantiation/common/instantiation'; +import { IInstantiationService, ServicesAccessor, IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation'; import { ITextModel } from 'vs/editor/common/model'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/parts/files/electron-browser/fileCommands'; @@ -47,7 +46,6 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { EditableExplorerItems } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; import { NewStatPlaceholder, ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; export interface IEditableData { @@ -55,12 +53,6 @@ export interface IEditableData { validator: IInputValidator; } -export interface IFileViewletState { - getEditableData(stat: ExplorerItem): IEditableData; - setEditable(stat: ExplorerItem, editableData: IEditableData): void; - clearEditable(stat: ExplorerItem): void; -} - export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -137,20 +129,18 @@ class TriggerRenameFileAction extends BaseFileAction { public static readonly ID = 'renameFile'; - private tree: ITree; private renameAction: BaseRenameAction; constructor( - tree: ITree, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IExplorerService private explorerService: IExplorerService ) { super(TriggerRenameFileAction.ID, TRIGGER_RENAME_LABEL, fileService, notificationService, textFileService); - this.tree = tree; this.element = element; this.renameAction = instantiationService.createInstance(RenameFileAction, element); this._updateEnablement(); @@ -168,51 +158,9 @@ class TriggerRenameFileAction extends BaseFileAction { return this.renameAction.validateFileName(this.element.parent, name); } - public run(context?: any): Promise { - if (!context) { - return Promise.reject(new Error('No context provided to BaseEnableFileRenameAction.')); - } - - const viewletState = context.viewletState; - if (!viewletState) { - return Promise.reject(new Error('Invalid viewlet state provided to BaseEnableFileRenameAction.')); - } - - const stat = context.stat; - if (!stat) { - return Promise.reject(new Error('Invalid stat provided to BaseEnableFileRenameAction.')); - } - - viewletState.setEditable(stat, { - action: this.renameAction, - validator: (value) => { - const message = this.validateFileName(value); - - if (!message) { - return null; - } - - return { - content: message, - formatContent: true, - type: MessageType.ERROR - }; - } - }); - - this.tree.refresh(stat, false).then(() => { - this.tree.setHighlight(stat); - - const unbind = this.tree.onDidChangeHighlight((e: IHighlightEvent) => { - if (!e.highlight) { - viewletState.clearEditable(stat); - this.tree.refresh(stat); - unbind.dispose(); - } - }); - }); - - return void 0; + public run(): Promise { + this.explorerService.setEditable(this.element, { validationMessage: this.validateFileName, action: this.renameAction }); + return Promise.resolve(void 0); } } @@ -311,14 +259,13 @@ export class BaseNewAction extends BaseFileAction { constructor( id: string, label: string, - private tree: AsyncDataTree, - private viewletState: IFileViewletState, private isFile: boolean, private renameAction: BaseRenameAction, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService + @ITextFileService textFileService: ITextFileService, + @IExplorerService private explorerService: IExplorerService ) { super(id, label, fileService, notificationService, textFileService); @@ -327,23 +274,13 @@ export class BaseNewAction extends BaseFileAction { } } - public run(context?: any): Promise { + public run(): Promise { let folder = this.presetFolder; if (!folder) { - const focusedElements = this.tree.getFocus(); - const focus = focusedElements.length ? focusedElements[0] : undefined; - - if (focus) { - folder = focus.isDirectory ? focus : focus.parent; - } else { - folder = this.tree.getNode(null).element; - } + folder = this.explorerService.roots[0]; } - if (!folder) { - return Promise.reject(new Error('Invalid parent folder to create.')); - } if (folder.isReadonly) { return Promise.reject(new Error('Parent folder is readonly.')); } @@ -352,33 +289,12 @@ export class BaseNewAction extends BaseFileAction { return Promise.resolve(new Error('Parent folder is already in the process of creating a file')); } - this.tree.reveal(folder, 0.5); - return this.tree.expand(folder).then(() => { - const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); + const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); - this.renameAction.element = stat; + this.renameAction.element = stat; + this.explorerService.setEditable(stat, { action: this.renameAction, validationMessage: value => this.renameAction.validateFileName(folder, value) }); - this.viewletState.setEditable(stat, { - action: this.renameAction, - validator: (value) => { - const message = this.renameAction.validateFileName(folder, value); - - if (!message) { - return null; - } - - return { - content: message, - formatContent: true, - type: MessageType.ERROR - }; - } - }); - - return this.tree.refresh(folder).then(() => { - return this.tree.expand(folder).then(() => this.tree.reveal(stat, 0.5)); - }); - }); + return Promise.resolve(void 0); } } @@ -386,15 +302,14 @@ export class BaseNewAction extends BaseFileAction { export class NewFileAction extends BaseNewAction { constructor( - tree: AsyncDataTree, - fileViewletState: EditableExplorerItems, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IExplorerService explorerService: IExplorerService ) { - super('explorer.newFile', NEW_FILE_LABEL, tree, fileViewletState, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, notificationService, textFileService); + super('explorer.newFile', NEW_FILE_LABEL, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, notificationService, textFileService, explorerService); this.class = 'explorer-action new-file'; this._updateEnablement(); @@ -405,15 +320,14 @@ export class NewFileAction extends BaseNewAction { export class NewFolderAction extends BaseNewAction { constructor( - tree: AsyncDataTree, - fileViewletState: EditableExplorerItems, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IExplorerService explorerService: IExplorerService ) { - super('explorer.newFolder', NEW_FOLDER_LABEL, tree, fileViewletState, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, notificationService, textFileService); + super('explorer.newFolder', NEW_FOLDER_LABEL, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, notificationService, textFileService, explorerService); this.class = 'explorer-action new-folder'; this._updateEnablement(); @@ -510,7 +424,6 @@ class BaseDeleteFileAction extends BaseFileAction { private skipConfirm: boolean; constructor( - private tree: ITree, private elements: ExplorerItem[], private useTrash: boolean, @IFileService fileService: IFileService, @@ -521,7 +434,6 @@ class BaseDeleteFileAction extends BaseFileAction { ) { super('moveFileToTrash', MOVE_FILE_TO_TRASH_LABEL, fileService, notificationService, textFileService); - this.tree = tree; this.useTrash = useTrash && elements.every(e => !paths.isUNC(e.resource.fsPath)); // on UNC shares there is no trash this._updateEnablement(); @@ -533,11 +445,6 @@ class BaseDeleteFileAction extends BaseFileAction { public run(): Promise { - // Remove highlight - if (this.tree) { - this.tree.clearHighlight(); - } - let primaryButton: string; if (this.useTrash) { primaryButton = isWindows ? nls.localize('deleteButtonLabelRecycleBin', "&&Move to Recycle Bin") : nls.localize({ key: 'deleteButtonLabelTrash', comment: ['&& denotes a mnemonic'] }, "&&Move to Trash"); @@ -634,48 +541,41 @@ class BaseDeleteFileAction extends BaseFileAction { } // Call function - const servicePromise = Promise.all(distinctElements.map(e => this.fileService.del(e.resource, { useTrash: this.useTrash, recursive: true }))).then(() => { - if (distinctElements[0].parent) { - this.tree.setFocus(distinctElements[0].parent); // move focus to parent - } - }, (error: any) => { - - // Handle error to delete file(s) from a modal confirmation dialog - let errorMessage: string; - let detailMessage: string; - let primaryButton: string; - if (this.useTrash) { - errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?"); - detailMessage = nls.localize('irreversible', "This action is irreversible!"); - primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently"); - } else { - errorMessage = toErrorMessage(error, false); - primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry"); - } - - return this.dialogService.confirm({ - message: errorMessage, - detail: detailMessage, - type: 'warning', - primaryButton - }).then(res => { - - // Focus back to tree - this.tree.domFocus(); - - if (res.confirmed) { - if (this.useTrash) { - this.useTrash = false; // Delete Permanently - } - - this.skipConfirm = true; - - return this.run(); + const servicePromise = Promise.all(distinctElements.map(e => this.fileService.del(e.resource, { useTrash: this.useTrash, recursive: true }))) + .then(undefined, (error: any) => { + // Handle error to delete file(s) from a modal confirmation dialog + let errorMessage: string; + let detailMessage: string; + let primaryButton: string; + if (this.useTrash) { + errorMessage = isWindows ? nls.localize('binFailed', "Failed to delete using the Recycle Bin. Do you want to permanently delete instead?") : nls.localize('trashFailed', "Failed to delete using the Trash. Do you want to permanently delete instead?"); + detailMessage = nls.localize('irreversible', "This action is irreversible!"); + primaryButton = nls.localize({ key: 'deletePermanentlyButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Delete Permanently"); + } else { + errorMessage = toErrorMessage(error, false); + primaryButton = nls.localize({ key: 'retryButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Retry"); } - return Promise.resolve(void 0); + return this.dialogService.confirm({ + message: errorMessage, + detail: detailMessage, + type: 'warning', + primaryButton + }).then(res => { + + if (res.confirmed) { + if (this.useTrash) { + this.useTrash = false; // Delete Permanently + } + + this.skipConfirm = true; + + return this.run(); + } + + return Promise.resolve(void 0); + }); }); - }); return servicePromise; }); @@ -734,21 +634,19 @@ class BaseDeleteFileAction extends BaseFileAction { /* Add File */ export class AddFilesAction extends BaseFileAction { - private tree: ITree; constructor( - tree: ITree, element: ExplorerItem, clazz: string, @IFileService fileService: IFileService, @IEditorService private editorService: IEditorService, @IDialogService private dialogService: IDialogService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService + @ITextFileService textFileService: ITextFileService, + @IExplorerService private explorerService: IExplorerService ) { super('workbench.files.action.addFile', nls.localize('addFiles', "Add Files"), fileService, notificationService, textFileService); - this.tree = tree; this.element = element; if (clazz) { @@ -759,102 +657,89 @@ export class AddFilesAction extends BaseFileAction { } public run(resourcesToAdd: URI[]): Promise { - const addPromise = Promise.resolve(null).then(() => { - if (resourcesToAdd && resourcesToAdd.length > 0) { + if (resourcesToAdd && resourcesToAdd.length > 0) { - // Find parent to add to - let targetElement: ExplorerItem; - if (this.element) { - targetElement = this.element; - } else { - // TODO@isidor - // const input: ExplorerItem | Model = this.tree.getInput(); - // targetElement = this.tree.getFocus() || (input instanceof Model ? input.roots[0] : input); - } - - if (!targetElement.isDirectory) { - targetElement = targetElement.parent; - } - - // Resolve target to check for name collisions and ask user - return this.fileService.resolveFile(targetElement.resource).then((targetStat: IFileStat) => { - - // Check for name collisions - const targetNames = new Set(); - targetStat.children.forEach((child) => { - targetNames.add(isLinux ? child.name : child.name.toLowerCase()); - }); - - let overwritePromise: Promise = Promise.resolve({ confirmed: true }); - if (resourcesToAdd.some(resource => { - return targetNames.has(!resources.hasToIgnoreCase(resource) ? resources.basename(resource) : resources.basename(resource).toLowerCase()); - })) { - const confirm: IConfirmation = { - message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), - detail: nls.localize('irreversible', "This action is irreversible!"), - primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' - }; - - overwritePromise = this.dialogService.confirm(confirm); - } - - return overwritePromise.then(res => { - if (!res.confirmed) { - return void 0; - } - - // Run add in sequence - const addPromisesFactory: ITask>[] = []; - resourcesToAdd.forEach(resource => { - addPromisesFactory.push(() => { - const sourceFile = resource; - const targetFile = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); - - // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents - // of the target file would replace the contents of the added file. since we already - // confirmed the overwrite before, this is OK. - let revertPromise: Promise = Promise.resolve(null); - if (this.textFileService.isDirty(targetFile)) { - revertPromise = this.textFileService.revertAll([targetFile], { soft: true }); - } - - return revertPromise.then(() => { - const target = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); - return this.fileService.copyFile(sourceFile, target, true).then(stat => { - - // if we only add one file, just open it directly - if (resourcesToAdd.length === 1) { - this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - } - }, error => this.onError(error)); - }); - }); - }); - - return sequence(addPromisesFactory); - }); - }); + // Find parent to add to + let targetElement: ExplorerItem; + if (this.element) { + targetElement = this.element; + } else { + targetElement = this.explorerService.roots[0]; } - return void 0; - }); + if (!targetElement.isDirectory) { + targetElement = targetElement.parent; + } - return addPromise.then(() => { - this.tree.clearHighlight(); - }, (error: any) => { - this.onError(error); - this.tree.clearHighlight(); - }); + // Resolve target to check for name collisions and ask user + return this.fileService.resolveFile(targetElement.resource).then((targetStat: IFileStat) => { + + // Check for name collisions + const targetNames = new Set(); + targetStat.children.forEach((child) => { + targetNames.add(isLinux ? child.name : child.name.toLowerCase()); + }); + + let overwritePromise: Promise = Promise.resolve({ confirmed: true }); + if (resourcesToAdd.some(resource => { + return targetNames.has(!resources.hasToIgnoreCase(resource) ? resources.basename(resource) : resources.basename(resource).toLowerCase()); + })) { + const confirm: IConfirmation = { + message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), + detail: nls.localize('irreversible', "This action is irreversible!"), + primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + + overwritePromise = this.dialogService.confirm(confirm); + } + + return overwritePromise.then(res => { + if (!res.confirmed) { + return void 0; + } + + // Run add in sequence + const addPromisesFactory: ITask>[] = []; + resourcesToAdd.forEach(resource => { + addPromisesFactory.push(() => { + const sourceFile = resource; + const targetFile = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); + + // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents + // of the target file would replace the contents of the added file. since we already + // confirmed the overwrite before, this is OK. + let revertPromise: Promise = Promise.resolve(null); + if (this.textFileService.isDirty(targetFile)) { + revertPromise = this.textFileService.revertAll([targetFile], { soft: true }); + } + + return revertPromise.then(() => { + const target = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); + return this.fileService.copyFile(sourceFile, target, true).then(stat => { + + // if we only add one file, just open it directly + if (resourcesToAdd.length === 1) { + this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + } + }, error => this.onError(error)); + }); + }); + }); + + return sequence(addPromisesFactory); + }); + }); + } + + return Promise.resolve(void 0); } } // Copy File/Folder class CopyFileAction extends BaseFileAction { - private tree: ITree; constructor( - tree: ITree, private elements: ExplorerItem[], @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @@ -864,7 +749,6 @@ class CopyFileAction extends BaseFileAction { ) { super('filesExplorer.copy', COPY_FILE_LABEL, fileService, notificationService, textFileService); - this.tree = tree; this._updateEnablement(); } @@ -873,13 +757,6 @@ class CopyFileAction extends BaseFileAction { // Write to clipboard as file/folder to copy this.clipboardService.writeResources(this.elements.map(e => e.resource)); - // Remove highlight - if (this.tree) { - this.tree.clearHighlight(); - } - - this.tree.domFocus(); - return Promise.resolve(null); } } @@ -889,24 +766,19 @@ class PasteFileAction extends BaseFileAction { public static readonly ID = 'filesExplorer.paste'; - private tree: ITree; - constructor( - tree: ITree, element: ExplorerItem, @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, @ITextFileService textFileService: ITextFileService, - @IEditorService private editorService: IEditorService + @IEditorService private editorService: IEditorService, + @IExplorerService private explorerService: IExplorerService ) { super(PasteFileAction.ID, PASTE_FILE_LABEL, fileService, notificationService, textFileService); - this.tree = tree; this.element = element; if (!this.element) { - // TODO@isidor - // const input: ExplorerItem | Model = this.tree.getInput(); - // this.element = input instanceof Model ? input.roots[0] : input; + this.element = this.explorerService.roots[0]; } this._updateEnablement(); } @@ -920,11 +792,6 @@ class PasteFileAction extends BaseFileAction { return this.fileService.resolveFile(fileToPaste).then(fileToPasteStat => { - // Remove highlight - if (this.tree) { - this.tree.clearHighlight(); - } - // Find target let target: ExplorerItem; if (this.element.resource.toString() === fileToPaste.toString()) { @@ -942,8 +809,6 @@ class PasteFileAction extends BaseFileAction { } return void 0; - }, error => this.onError(error)).then(() => { - this.tree.domFocus(); }); }, error => { this.onError(new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile"))); @@ -953,11 +818,9 @@ class PasteFileAction extends BaseFileAction { // Duplicate File/Folder export class DuplicateFileAction extends BaseFileAction { - private tree: ITree; private target: ExplorerItem; constructor( - tree: ITree, fileToDuplicate: ExplorerItem, target: ExplorerItem, @IFileService fileService: IFileService, @@ -967,19 +830,12 @@ export class DuplicateFileAction extends BaseFileAction { ) { super('workbench.files.action.duplicateFile', nls.localize('duplicateFile', "Duplicate"), fileService, notificationService, textFileService); - this.tree = tree; this.element = fileToDuplicate; this.target = (target && target.isDirectory) ? target : fileToDuplicate.parent; this._updateEnablement(); } public run(): Promise { - - // Remove highlight - if (this.tree) { - this.tree.clearHighlight(); - } - // Copy File const result = this.fileService.copyFile(this.element.resource, findValidPasteFileTarget(this.target, { resource: this.element.resource, isDirectory: this.element.isDirectory })).then(stat => { if (!stat.isDirectory) { @@ -1544,7 +1400,7 @@ function getContext(listWidget: ListWidget): IExplorerContext { // TODO@isidor these commands are calling into actions due to the complex inheritance action structure. // It should be the other way around, that actions call into commands. -function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature3, EditableExplorerItems, ExplorerItem, Action>): Promise { +function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature1): Promise { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); const viewletService = accessor.get(IViewletService); @@ -1559,7 +1415,7 @@ function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: ICons if (explorerView && explorerView.isVisible() && explorerView.isExpanded()) { explorerView.focus(); const { stat } = getContext(listService.lastFocusedList); - const action = instantationService.createInstance(constructor, listService.lastFocusedList, explorerView.editableExplorerItems, stat); + const action = instantationService.createInstance(constructor, stat); return action.run(); } @@ -1587,8 +1443,8 @@ export const renameHandler = (accessor: ServicesAccessor) => { const listService = accessor.get(IListService); const explorerContext = getContext(listService.lastFocusedList); - const renameAction = instantationService.createInstance(TriggerRenameFileAction, listService.lastFocusedList, explorerContext.stat); - return renameAction.run(explorerContext); + const renameAction = instantationService.createInstance(TriggerRenameFileAction, explorerContext.stat); + return renameAction.run(); }; export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { @@ -1597,7 +1453,7 @@ export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; - const moveFileToTrashAction = instantationService.createInstance(BaseDeleteFileAction, listService.lastFocusedList, stats, true); + const moveFileToTrashAction = instantationService.createInstance(BaseDeleteFileAction, stats, true); return moveFileToTrashAction.run(); }; @@ -1607,7 +1463,7 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => { const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; - const deleteFileAction = instantationService.createInstance(BaseDeleteFileAction, listService.lastFocusedList, stats, false); + const deleteFileAction = instantationService.createInstance(BaseDeleteFileAction, stats, false); return deleteFileAction.run(); }; @@ -1617,7 +1473,7 @@ export const copyFileHandler = (accessor: ServicesAccessor) => { const explorerContext = getContext(listService.lastFocusedList); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; - const copyFileAction = instantationService.createInstance(CopyFileAction, listService.lastFocusedList, stats); + const copyFileAction = instantationService.createInstance(CopyFileAction, stats); return copyFileAction.run(); }; @@ -1628,7 +1484,7 @@ export const pasteFileHandler = (accessor: ServicesAccessor) => { const explorerContext = getContext(listService.lastFocusedList); return Promise.all(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => { - const pasteFileAction = instantationService.createInstance(PasteFileAction, listService.lastFocusedList, explorerContext.stat); + const pasteFileAction = instantationService.createInstance(PasteFileAction, explorerContext.stat); return pasteFileAction.run(toCopy); })); }; 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 045a3083d6b..6127810b457 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -19,7 +19,6 @@ import { toResource } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import * as DOM from 'vs/base/browser/dom'; import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; -import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ExplorerDecorationsProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -40,7 +39,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, EditableExplorerItems as EditableExplorerItems, FilesFilter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; @@ -50,10 +49,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; -export interface IExplorerViewOptions extends IViewletViewOptions { - fileViewletState: EditableExplorerItems; -} - function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): glob.IExpression { const scope = root ? { resource: root } : void 0; const configuration = configurationService.getValue(scope); @@ -70,7 +65,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; private isCreated: boolean; - private _editableExplorerItems: EditableExplorerItems; private explorerRefreshDelayer: ThrottledDelayer; @@ -87,7 +81,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private isDisposed = false; constructor( - options: IExplorerViewOptions, + options: IViewletPanelOptions, @INotificationService private notificationService: INotificationService, @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService private instantiationService: IInstantiationService, @@ -110,7 +104,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { ) { super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); - this._editableExplorerItems = new EditableExplorerItems(); this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); this.resourceContext = instantiationService.createInstance(ResourceContextKey); @@ -160,10 +153,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { // noop } - get editableExplorerItems(): EditableExplorerItems { - return this._editableExplorerItems; - } - // Memoized locals @memoize private get fileEventsFilter(): ResourceGlobMatcher { const fileEventsFilter = this.instantiationService.createInstance( @@ -228,8 +217,8 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { getActions(): IAction[] { const actions: Action[] = []; - actions.push(this.instantiationService.createInstance(NewFileAction, this.tree, this._editableExplorerItems, null)); - actions.push(this.instantiationService.createInstance(NewFolderAction, this.tree, this._editableExplorerItems, null)); + actions.push(this.instantiationService.createInstance(NewFileAction, null)); + actions.push(this.instantiationService.createInstance(NewFolderAction, null)); actions.push(this.instantiationService.createInstance(RefreshViewExplorerAction, this, 'explorer-action refresh-explorer')); actions.push(this.instantiationService.createInstance(CollapseAction2, this.tree, true, 'explorer-action collapse-explorer')); @@ -373,7 +362,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private createTree(container: HTMLElement): void { this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); - const filesRenderer = this.instantiationService.createInstance(FilesRenderer, this._editableExplorerItems); + const filesRenderer = this.instantiationService.createInstance(FilesRenderer); this.disposables.push(filesRenderer); this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer], 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 3a6fb501de0..652faa274fa 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -16,7 +16,6 @@ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IDataSource } from 'vs/base/browser/ui/tree/tree'; -import { IFileViewletState, IEditableData } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -33,6 +32,7 @@ import { rtrim } from 'vs/base/common/strings'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; +import { IAction } from 'vs/base/common/actions'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -118,45 +118,20 @@ export interface IFileTemplateData { container: HTMLElement; } -export class EditableExplorerItems implements IFileViewletState { - private editableStats: Map; - - constructor() { - this.editableStats = new Map(); - } - - public getEditableData(stat: ExplorerItem): IEditableData { - return this.editableStats.get(stat); - } - - public setEditable(stat: ExplorerItem, editableData: IEditableData): void { - if (editableData) { - this.editableStats.set(stat, editableData); - } - } - - public clearEditable(stat: ExplorerItem): void { - this.editableStats.delete(stat); - } -} - export class FilesRenderer implements ITreeRenderer, IDisposable { static readonly ID = 'file'; - private state: EditableExplorerItems; private config: IFilesConfiguration; private configListener: IDisposable; constructor( - state: EditableExplorerItems, @IContextViewService private contextViewService: IContextViewService, @IInstantiationService private instantiationService: IInstantiationService, @IThemeService private themeService: IThemeService, @IConfigurationService private configurationService: IConfigurationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService - + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IExplorerService private explorerService: IExplorerService ) { - this.state = state; this.config = this.configurationService.getValue(); this.configListener = this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('explorer')) { @@ -179,7 +154,7 @@ export class FilesRenderer implements ITreeRenderer, index: number, templateData: IFileTemplateData): void { templateData.elementDisposable.dispose(); const stat = element.element; - const editableData: IEditableData = this.state.getEditableData(stat); + const editableData = this.explorerService.getEditableData(stat); // File Label if (!editableData) { @@ -206,7 +181,7 @@ export class FilesRenderer implements ITreeRenderer string, action: IAction }): void { // Use a file label only for the icon next to the input box const label = this.instantiationService.createInstance(FileLabel, container, void 0); @@ -222,7 +197,18 @@ export class FilesRenderer implements ITreeRenderer { + const content = editableData.validationMessage(value); + if (!content) { + return null; + } + + return { + content, + formatContent: true, + type: MessageType.ERROR + }; + } }, ariaLabel: localize('fileInputAriaLabel', "Type file name. Press Enter to confirm or Escape to cancel.") }); From e69d6ce6d4c20b3d736889d36997210ed70d5517 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Mon, 17 Dec 2018 17:19:17 -0800 Subject: [PATCH 0023/1066] 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 6a48614c49c3226a4dee7e1a882f0a039fe61ec3 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 18 Dec 2018 11:30:43 +0100 Subject: [PATCH 0024/1066] explorer: adopt to tree interface changes --- .../workbench/parts/files/electron-browser/fileActions.ts | 2 +- .../parts/files/electron-browser/views/explorerView.ts | 6 +++--- .../parts/files/electron-browser/views/explorerViewer.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 98590515c05..37b83cd2abc 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1389,7 +1389,7 @@ interface IExplorerContext { function getContext(listWidget: ListWidget): IExplorerContext { // These commands can only be triggered when explorer viewlet is visible so get it using the active viewlet - const tree = >listWidget; + const tree = >listWidget; const focus = tree.getFocus(); const stat = focus.length ? focus[0] : undefined; const selection = tree.getSelection(); 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 6127810b457..64ae48d7ed6 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -62,7 +62,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; private isCreated: boolean; @@ -831,7 +831,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } }); - return this.tree.refresh(null); + return this.tree.setInput(null); }); } @@ -844,7 +844,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { ExplorerItem.mergeLocalWithDisk(modelStat, this.explorerService.roots[index]); } - return this.tree.refresh(null); + return this.tree.setInput(null); }))); } 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 652faa274fa..0e37fb2ba4b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -15,7 +15,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -47,7 +47,7 @@ export class ExplorerDelegate implements IListVirtualDelegate { } } -export class ExplorerDataSource implements IDataSource { +export class ExplorerDataSource implements IAsyncDataSource { constructor( @IExplorerService private explorerService: IExplorerService, From 4d048e02d76f0d13d8076509b4a1121e23c94698 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 18 Dec 2018 11:37:27 +0100 Subject: [PATCH 0025/1066] explorer: properly handle mouse middle button --- .../electron-browser/views/explorerView.ts | 5 +- .../electron-browser/views/explorerViewer.ts | 139 ------------------ 2 files changed, 4 insertions(+), 140 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 64ae48d7ed6..a2e77e85eb9 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -405,8 +405,11 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (selection && selection.length === 1) { let isDoubleClick = false; let sideBySide = false; + let isMiddleClick = false; + if (e.browserEvent instanceof MouseEvent) { isDoubleClick = e.browserEvent.detail === 2; + isMiddleClick = e.browserEvent.button === 1; sideBySide = this.tree.useAltAsMultipleSelectionModifier ? (e.browserEvent.ctrlKey || e.browserEvent.metaKey) : e.browserEvent.altKey; } @@ -418,7 +421,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } }*/ this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: !isDoubleClick, pinned: isDoubleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } })); 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 0e37fb2ba4b..6a9ba0ff1ce 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -384,145 +384,6 @@ export class FilesFilter implements ITreeFilter { } } -// Explorer Controller -// export class FileController extends WorkbenchTreeController implements IDisposable { -// private fileCopiedContextKey: IContextKey; -// private contributedContextMenu: IMenu; -// private previousSelectionRangeStop: ExplorerItem; - -// constructor( -// @IEditorService private editorService: IEditorService, -// @IContextMenuService private contextMenuService: IContextMenuService, -// @ITelemetryService private telemetryService: ITelemetryService, -// @IMenuService private menuService: IMenuService, -// @IContextKeyService contextKeyService: IContextKeyService, -// @IClipboardService private clipboardService: IClipboardService, -// @IConfigurationService configurationService: IConfigurationService -// ) { -// super({ clickBehavior: ClickBehavior.ON_MOUSE_UP /* do not change to not break DND */ }, configurationService); - -// this.fileCopiedContextKey = FileCopiedContext.bindTo(contextKeyService); -// } - -// public onLeftClick(tree: WorkbenchTree, stat: ExplorerItem | Model, event: IMouseEvent, origin: string = 'mouse'): boolean { -// const payload = { origin: origin }; -// const isDoubleClick = (origin === 'mouse' && event.detail === 2); - -// // Handle Highlight Mode -// if (tree.getHighlight()) { - -// // Cancel Event -// event.preventDefault(); -// event.stopPropagation(); - -// tree.clearHighlight(payload); - -// return false; -// } - -// // Handle root -// if (stat instanceof Model) { -// tree.clearFocus(payload); -// tree.clearSelection(payload); - -// return false; -// } - -// // Cancel Event -// const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; -// if (!isMouseDown) { -// event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise -// } -// event.stopPropagation(); - -// // Set DOM focus -// tree.domFocus(); -// if (stat instanceof NewStatPlaceholder) { -// return true; -// } - -// // Allow to multiselect -// if ((tree.useAltAsMultipleSelectionModifier && event.altKey) || !tree.useAltAsMultipleSelectionModifier && (event.ctrlKey || event.metaKey)) { -// const selection = tree.getSelection(); -// this.previousSelectionRangeStop = undefined; -// if (selection.indexOf(stat) >= 0) { -// tree.setSelection(selection.filter(s => s !== stat)); -// } else { -// tree.setSelection(selection.concat(stat)); -// tree.setFocus(stat, payload); -// } -// } - -// // Allow to unselect -// else if (event.shiftKey) { -// const focus = tree.getFocus(); -// if (focus) { -// if (this.previousSelectionRangeStop) { -// tree.deselectRange(stat, this.previousSelectionRangeStop); -// } -// tree.selectRange(focus, stat, payload); -// this.previousSelectionRangeStop = stat; -// } -// } - -// // Select, Focus and open files -// else { - -// // Expand / Collapse -// if (isDoubleClick || this.openOnSingleClick || this.isClickOnTwistie(event)) { -// tree.toggleExpansion(stat, event.altKey); -// this.previousSelectionRangeStop = undefined; -// } - -// const preserveFocus = !isDoubleClick; -// tree.setFocus(stat, payload); - -// if (isDoubleClick) { -// event.preventDefault(); // focus moves to editor, we need to prevent default -// } - -// tree.setSelection([stat], payload); - -// if (!stat.isDirectory && (isDoubleClick || this.openOnSingleClick)) { -// let sideBySide = false; -// if (event) { -// sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; -// } - -// this.openEditor(stat, { preserveFocus, sideBySide, pinned: isDoubleClick }); -// } -// } - -// return true; -// } - -// public onMouseMiddleClick(tree: WorkbenchTree, element: ExplorerItem | Model, event: IMouseEvent): boolean { -// let sideBySide = false; -// if (event) { -// sideBySide = tree.useAltAsMultipleSelectionModifier ? (event.ctrlKey || event.metaKey) : event.altKey; -// } -// if (element instanceof ExplorerItem && !element.isDirectory) { -// this.openEditor(element, { preserveFocus: true, sideBySide, pinned: true }); -// } - -// return true; -// } - -// public openEditor(stat: ExplorerItem, options: { preserveFocus: boolean; sideBySide: boolean; pinned: boolean; }): void { -// if (stat && !stat.isDirectory) { -// /* __GDPR__ -// "workbenchActionExecuted" : { -// "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, -// "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } -// } -// */ -// this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - -// this.editorService.openEditor({ resource: stat.resource, options }, options.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); -// } -// } -// } - // // Explorer Sorter // export class FileSorter implements ISorter { // private toDispose: IDisposable[]; From 688595bb5c6a07ef94d587d02cf153606a1873da Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 18 Dec 2018 15:08:04 +0100 Subject: [PATCH 0026/1066] explorer: split logic between view, service and model --- src/vs/workbench/parts/files/browser/files.ts | 2 +- .../{explorerService.ts => explorerModel.ts} | 45 +-- src/vs/workbench/parts/files/common/files.ts | 13 +- .../files/electron-browser/explorerService.ts | 270 ++++++++++++++++++ .../files/electron-browser/fileActions.ts | 2 +- .../electron-browser/files.contribution.ts | 2 +- .../electron-browser/views/explorerView.ts | 261 +---------------- .../electron-browser/views/explorerViewer.ts | 2 +- ...rService.test.ts => explorerModel.test.ts} | 2 +- 9 files changed, 296 insertions(+), 303 deletions(-) rename src/vs/workbench/parts/files/common/{explorerService.ts => explorerModel.ts} (89%) create mode 100644 src/vs/workbench/parts/files/electron-browser/explorerService.ts rename src/vs/workbench/parts/files/test/electron-browser/{explorerService.test.ts => explorerModel.test.ts} (99%) diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index ac5b43d9cca..93bf737781c 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -5,12 +5,12 @@ import { URI } from 'vs/base/common/uri'; import { IListService } from 'vs/platform/list/browser/listService'; -import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; import { OpenEditor } from 'vs/workbench/parts/files/common/files'; import { toResource } from 'vs/workbench/common/editor'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; // Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed diff --git a/src/vs/workbench/parts/files/common/explorerService.ts b/src/vs/workbench/parts/files/common/explorerModel.ts similarity index 89% rename from src/vs/workbench/parts/files/common/explorerService.ts rename to src/vs/workbench/parts/files/common/explorerModel.ts index bc19d18e70e..b98b17a1019 100644 --- a/src/vs/workbench/parts/files/common/explorerService.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -4,28 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { Event, Emitter } from 'vs/base/common/event'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { ResourceMap } from 'vs/base/common/map'; import { isLinux } from 'vs/base/common/platform'; import { IFileStat } from 'vs/platform/files/common/files'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings'; import { coalesce } from 'vs/base/common/arrays'; -import { IExplorerService } from 'vs/workbench/parts/files/common/files'; -import { IAction } from 'vs/base/common/actions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -export class ExplorerService implements IExplorerService { - _serviceBrand: any; +export class ExplorerModel implements IDisposable { private _roots: ExplorerItem[]; - private _onDidChangeEditable = new Emitter(); private _listener: IDisposable; - private editableStats = new Map string, action: IAction }>(); - constructor(@IWorkspaceContextService private contextService: IWorkspaceContextService) { + constructor(private contextService: IWorkspaceContextService) { const setRoots = () => this._roots = this.contextService.getWorkspace().folders .map(folder => new ExplorerItem(folder.uri, undefined, false, false, true, folder.name)); this._listener = this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); @@ -36,19 +30,6 @@ export class ExplorerService implements IExplorerService { return this._roots; } - get onDidChangeEditable(): Event { - return this._onDidChangeEditable.event; - } - - setEditable(stat: ExplorerItem, data: { validationMessage: (value: string) => string, action: IAction }): void { - this.editableStats.set(stat, data); - this._onDidChangeEditable.fire(stat); - } - - getEditableData(stat: ExplorerItem): { validationMessage: (value: string) => string, action: IAction } | undefined { - return this.editableStats.get(stat); - } - /** * Returns an array of child stat from this stat that matches with the provided path. * Starts matching from the first root. @@ -314,21 +295,11 @@ export class ExplorerItem { /** * Moves this element under a new parent element. */ - move(newParent: ExplorerItem, fnBetweenStates?: (callback: () => void) => void, fnDone?: () => void): void { - if (!fnBetweenStates) { - fnBetweenStates = (cb: () => void) => { cb(); }; - } - + move(newParent: ExplorerItem): void { this.parent.removeChild(this); - - fnBetweenStates(() => { - newParent.removeChild(this); // make sure to remove any previous version of the file if any - newParent.addChild(this); - this.updateResource(true); - if (fnDone) { - fnDone(); - } - }); + newParent.removeChild(this); // make sure to remove any previous version of the file if any + newParent.addChild(this); + this.updateResource(true); } private updateResource(recursive: boolean): void { diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 649d7dd737d..3f63a17f4d7 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -21,9 +21,9 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; import { Schemas } from 'vs/base/common/network'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { IAction } from 'vs/base/common/actions'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; /** * Explorer viewlet id. @@ -42,15 +42,20 @@ export interface IExplorerView { select(resource: URI, reveal?: boolean): void; } +export interface IEditableData { + validationMessage: (value: string) => string; + action: IAction; +} + export interface IExplorerService { _serviceBrand: any; readonly roots: ExplorerItem[]; + readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; - setEditable(stat: ExplorerItem, data: { validationMessage: (value: string) => string, action: IAction }): void; - getEditableData(stat: ExplorerItem): { validationMessage: (value: string) => string, action: IAction } | undefined; + setEditable(stat: ExplorerItem, data: IEditableData): void; + getEditableData(stat: ExplorerItem): IEditableData | undefined; findClosest(resource: URI): ExplorerItem | null; - findAll(resource: URI): ExplorerItem[]; } export const IExplorerService = createDecorator('explorerService'); diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts new file mode 100644 index 00000000000..95dc09812f7 --- /dev/null +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -0,0 +1,270 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IExplorerService, IEditableData, IFilesConfiguration, SortOrder, SortOrderConfiguration } from 'vs/workbench/parts/files/common/files'; +import { ExplorerItem, ExplorerModel } from 'vs/workbench/parts/files/common/explorerModel'; +import { URI } from 'vs/base/common/uri'; +import { FileOperationEvent, FileOperation, IFileStat, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType } from 'vs/platform/files/common/files'; +import { dirname } from 'vs/base/common/resources'; +import { memoize } from 'vs/base/common/decorators'; +import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; +import { IExpression } from 'vs/base/common/glob'; + +function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { + const scope = root ? { resource: root } : void 0; + const configuration = configurationService.getValue(scope); + + return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); +} + +export class ExplorerService implements IExplorerService { + _serviceBrand: any; + + private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first + + private _onDidChangeItem = new Emitter(); + private _onDidChangeEditable = new Emitter(); + private disposables: IDisposable[] = []; + private editableStats = new Map(); + private sortOrder: SortOrder; + + constructor( + @IFileService private fileService: IFileService, + @IInstantiationService private instantiationService: IInstantiationService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { } + + get roots(): ExplorerItem[] { + return this.model.roots; + } + + get onDidChangeItem(): Event { + return this._onDidChangeItem.event; + } + + get onDidChangeEditable(): Event { + return this._onDidChangeEditable.event; + } + + // Memoized locals + @memoize private get fileEventsFilter(): ResourceGlobMatcher { + const fileEventsFilter = this.instantiationService.createInstance( + ResourceGlobMatcher, + (root: URI) => getFileEventsExcludes(this.configurationService, root), + (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) + ); + this.disposables.push(fileEventsFilter); + + return fileEventsFilter; + } + + @memoize get model(): ExplorerModel { + const model = new ExplorerModel(this.contextService); + this.disposables.push(model); + this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); + this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); + this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); + + return model; + } + + findClosest(resource: URI): ExplorerItem { + return this.model.findClosest(resource); + } + + setEditable(stat: ExplorerItem, data: IEditableData): void { + this.editableStats.set(stat, data); + this._onDidChangeEditable.fire(stat); + } + + getEditableData(stat: ExplorerItem): IEditableData | undefined { + return this.editableStats.get(stat); + } + + private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { + const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + if (this.sortOrder !== configSortOrder) { + this.sortOrder = configSortOrder; + this.roots.forEach(r => this._onDidChangeItem.fire(r)); + } + } + + // File events + private onFileOperation(e: FileOperationEvent): void { + // Add + if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { + const addedElement = e.target; + const parentResource = dirname(addedElement.resource); + const parents = this.model.findAll(parentResource); + + if (parents.length) { + + // Add the new file to its parent (Model) + parents.forEach(p => { + // We have to check if the parent is resolved #29177 + const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(null) : this.fileService.resolveFile(p.resource); + thenable.then(stat => { + if (stat) { + const modelStat = ExplorerItem.create(stat, p.root); + ExplorerItem.mergeLocalWithDisk(modelStat, p); + } + + const childElement = ExplorerItem.create(addedElement, p.root); + // Make sure to remove any previous version of the file if any + p.removeChild(childElement); + p.addChild(childElement); + // Refresh the Parent (View) + this._onDidChangeItem.fire(p); + }); + }); + } + } + + // Move (including Rename) + else if (e.operation === FileOperation.MOVE) { + const oldResource = e.resource; + const newElement = e.target; + const oldParentResource = dirname(oldResource); + const newParentResource = dirname(newElement.resource); + + // Handle Rename + if (oldParentResource && newParentResource && oldParentResource.toString() === newParentResource.toString()) { + const modelElements = this.model.findAll(oldResource); + modelElements.forEach(modelElement => { + // Rename File (Model) + modelElement.rename(newElement); + this._onDidChangeItem.fire(modelElement.parent); + }); + } + + // Handle Move + else if (oldParentResource && newParentResource) { + const newParents = this.model.findAll(newParentResource); + const modelElements = this.model.findAll(oldResource); + + if (newParents.length && modelElements.length) { + // Move in Model + modelElements.forEach((modelElement, index) => { + const oldParent = modelElement.parent; + this._onDidChangeItem.fire(oldParent); + this._onDidChangeItem.fire(newParents[index]); + }); + } + } + } + + // Delete + else if (e.operation === FileOperation.DELETE) { + const modelElements = this.model.findAll(e.resource); + modelElements.forEach(element => { + if (element.parent) { + const parent = element.parent; + // Remove Element from Parent (Model) + parent.removeChild(element); + // Refresh Parent (View) + this._onDidChangeItem.fire(parent); + } + }); + } + } + + private onFileChanges(e: FileChangesEvent): void { + // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) + // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might + // be fired first over the other or not at all. + setTimeout(() => { + // Filter to the ones we care + e = this.filterToViewRelevantEvents(e); + + // Handle added files/folders + const added = e.getAdded(); + if (added.length) { + + // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it + const ignoredPaths: { [resource: string]: boolean } = <{ [resource: string]: boolean }>{}; + for (let i = 0; i < added.length; i++) { + const change = added[i]; + + // Find parent + const parent = dirname(change.resource); + + // Continue if parent was already determined as to be ignored + if (ignoredPaths[parent.toString()]) { + continue; + } + + // Compute if parent is visible and added file not yet part of it + const parentStat = this.model.findClosest(parent); + if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { + this._onDidChangeItem.fire(parentStat); + } + + // Keep track of path that can be ignored for faster lookup + if (!parentStat || !parentStat.isDirectoryResolved) { + ignoredPaths[parent.toString()] = true; + } + } + } + + // Handle deleted files/folders + const deleted = e.getDeleted(); + if (deleted.length) { + + // Check deleted: Refresh if deleted file/folder part of resolved root + for (let j = 0; j < deleted.length; j++) { + const del = deleted[j]; + const item = this.model.findClosest(del.resource); + if (item) { + this._onDidChangeItem.fire(item.parent); + } + } + } + + // Handle updated files/folders if we sort by modified + if (this.sortOrder === SortOrderConfiguration.MODIFIED) { + const updated = e.getUpdated(); + + // Check updated: Refresh if updated file/folder part of resolved root + for (let j = 0; j < updated.length; j++) { + const upd = updated[j]; + const item = this.model.findClosest(upd.resource); + + if (item) { + this._onDidChangeItem.fire(item.parent); + } + } + } + + }, ExplorerService.EXPLORER_FILE_CHANGES_REACT_DELAY); + } + + private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { + return new FileChangesEvent(e.changes.filter(change => { + if (change.type === FileChangeType.UPDATED && this.sortOrder !== SortOrderConfiguration.MODIFIED) { + return false; // we only are about updated if we sort by modified time + } + + if (!this.contextService.isInsideWorkspace(change.resource)) { + return false; // exclude changes for resources outside of workspace + } + + if (this.fileEventsFilter.matches(change.resource)) { + return false; // excluded via files.exclude setting + } + + return true; + })); + } + + dispose(): void { + dispose(this.disposables); + } +} diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 37b83cd2abc..4246f210ffb 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -46,7 +46,7 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { NewStatPlaceholder, ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; +import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; export interface IEditableData { action: IAction; diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index 451b36ca422..2399ebd3771 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -36,7 +36,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { nativeSep } from 'vs/base/common/paths'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ExplorerService } from 'vs/workbench/parts/files/common/explorerService'; +import { ExplorerService } from 'vs/workbench/parts/files/electron-browser/explorerService'; // Viewlet Action export class OpenExplorerViewletAction extends ShowViewletAction { 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 a2e77e85eb9..9213168c8b8 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -9,11 +9,10 @@ import * as perf from 'vs/base/common/performance'; import { ThrottledDelayer, sequence, ignoreErrors } from 'vs/base/common/async'; import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; -import * as glob from 'vs/base/common/glob'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, SortOrderConfiguration, SortOrder, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; -import { FileOperation, FileOperationEvent, IResolveFileOptions, FileChangeType, FileChangesEvent, IFileService, FILES_EXCLUDE_CONFIG, IFileStat } from 'vs/platform/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; +import { IResolveFileOptions, IFileService } from 'vs/platform/files/common/files'; import { RefreshViewExplorerAction, NewFolderAction, NewFileAction, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { toResource } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -29,7 +28,6 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { isLinux } from 'vs/base/common/platform'; import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; @@ -47,19 +45,11 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; - -function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): glob.IExpression { - const scope = root ? { resource: root } : void 0; - const configuration = configurationService.getValue(scope); - - return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); -} +import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; export class ExplorerView extends ViewletPanel implements IExplorerView { static readonly ID: string = 'workbench.explorer.fileView'; - private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes private tree: WorkbenchAsyncDataTree; @@ -74,7 +64,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { private rootContext: IContextKey; private shouldRefresh: boolean; - private sortOrder: SortOrder; private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; @@ -154,17 +143,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } // Memoized locals - @memoize private get fileEventsFilter(): ResourceGlobMatcher { - const fileEventsFilter = this.instantiationService.createInstance( - ResourceGlobMatcher, - (root: URI) => getFileEventsExcludes(this.configurationService, root), - (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) - ); - this.disposables.push(fileEventsFilter); - - return fileEventsFilter; - } - @memoize private get contributedContextMenu(): IMenu { const contributedContextMenu = this.menuService.createMenu(MenuId.ExplorerContext, this.tree.contextKeyService); this.disposables.push(contributedContextMenu); @@ -297,7 +275,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (visible) { DOM.show(this.tree.getHTMLElement()); // If a refresh was requested and we are now visible, run it - let refreshPromise: Promise = Promise.resolve(null); + let refreshPromise = Promise.resolve(null); if (this.shouldRefresh) { refreshPromise = this.doRefresh(); this.shouldRefresh = false; // Reset flag @@ -383,9 +361,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); ExplorerFocusedContext.bindTo(this.tree.contextKeyService); - // Update Viewer based on File Change Events - this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); - this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); // Update resource context based on focused element this.disposables.push(this.tree.onDidChangeFocus(e => { @@ -451,12 +426,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { needsRefresh = this.filter.updateConfiguration(); } - const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; - if (this.sortOrder !== configSortOrder) { - this.sortOrder = configSortOrder; - needsRefresh = true; - } - if (event && !needsRefresh) { needsRefresh = event.affectsConfiguration('explorer.decorations.colors') || event.affectsConfiguration('explorer.decorations.badges'); @@ -468,228 +437,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } - private onFileOperation(e: FileOperationEvent): void { - if (!this.isCreated) { - return; // ignore if not yet created - } - - // Add - if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { - const addedElement = e.target; - const parentResource = resources.dirname(addedElement.resource); - const parents = this.explorerService.findAll(parentResource); - - if (parents.length) { - - // Add the new file to its parent (Model) - parents.forEach(p => { - // We have to check if the parent is resolved #29177 - const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(null) : this.fileService.resolveFile(p.resource); - thenable.then(stat => { - if (stat) { - const modelStat = ExplorerItem.create(stat, p.root); - ExplorerItem.mergeLocalWithDisk(modelStat, p); - } - - const childElement = ExplorerItem.create(addedElement, p.root); - // Make sure to remove any previous version of the file if any - p.removeChild(childElement); - p.addChild(childElement); - // Refresh the Parent (View) - this.tree.refresh(p).then(() => { - // Reveal and focus new element - this.tree.reveal(childElement, 0.5); - this.tree.setFocus([childElement]); - }); - }); - }); - } - } - - // Move (including Rename) - else if (e.operation === FileOperation.MOVE) { - const oldResource = e.resource; - const newElement = e.target; - - const oldParentResource = resources.dirname(oldResource); - const newParentResource = resources.dirname(newElement.resource); - - // Only update focus if renamed/moved element is selected - let restoreFocus = false; - const focusedElements = this.tree.getFocus(); - const focus = focusedElements && focusedElements.length ? focusedElements[0] : undefined; - if (focus && focus.resource && focus.resource.toString() === oldResource.toString()) { - restoreFocus = true; - } - - let isExpanded = false; - // Handle Rename - if (oldParentResource && newParentResource && oldParentResource.toString() === newParentResource.toString()) { - const modelElements = this.explorerService.findAll(oldResource); - modelElements.forEach(modelElement => { - //Check if element is expanded - isExpanded = !this.tree.isCollapsed(modelElement); - // Rename File (Model) - modelElement.rename(newElement); - - // Update Parent (View) - this.tree.refresh(modelElement.parent).then(() => { - - // Select in Viewer if set - if (restoreFocus) { - this.tree.setFocus([modelElement]); - } - //Expand the element again - if (isExpanded) { - this.tree.expand(modelElement); - } - }); - }); - } - - // Handle Move - else if (oldParentResource && newParentResource) { - const newParents = this.explorerService.findAll(newParentResource); - const modelElements = this.explorerService.findAll(oldResource); - - if (newParents.length && modelElements.length) { - - // Move in Model - modelElements.forEach((modelElement, index) => { - const oldParent = modelElement.parent; - modelElement.move(newParents[index], (callback: () => void) => { - // Update old parent - this.tree.refresh(oldParent).then(callback); - }, () => { - // Update new parent - this.tree.refresh(newParents[index], true).then(() => this.tree.expand(newParents[index])); - }); - }); - } - } - } - - // Delete - else if (e.operation === FileOperation.DELETE) { - const modelElements = this.explorerService.findAll(e.resource); - modelElements.forEach(element => { - if (element.parent) { - const parent = element.parent; - // Remove Element from Parent (Model) - parent.removeChild(element); - - // Refresh Parent (View) - const restoreFocus = document.activeElement === this.tree.getHTMLElement(); - this.tree.refresh(parent).then(() => { - - // Ensure viewer has keyboard focus if event originates from viewer - if (restoreFocus) { - this.tree.domFocus(); - } - }); - } - }); - } - } - - private onFileChanges(e: FileChangesEvent): void { - // Check if an explorer refresh is necessary (delayed to give internal events a chance to react first) - // Note: there is no guarantee when the internal events are fired vs real ones. Code has to deal with the fact that one might - // be fired first over the other or not at all. - setTimeout(() => { - if (!this.shouldRefresh && this.shouldRefreshFromEvent(e)) { - this.refreshFromEvent(); - } - }, ExplorerView.EXPLORER_FILE_CHANGES_REACT_DELAY); - } - - private shouldRefreshFromEvent(e: FileChangesEvent): boolean { - if (!this.isCreated) { - return false; - } - - // Filter to the ones we care - e = this.filterToViewRelevantEvents(e); - - // Handle added files/folders - const added = e.getAdded(); - if (added.length) { - - // Check added: Refresh if added file/folder is not part of resolved root and parent is part of it - const ignoredPaths: { [resource: string]: boolean } = <{ [resource: string]: boolean }>{}; - for (let i = 0; i < added.length; i++) { - const change = added[i]; - - // Find parent - const parent = resources.dirname(change.resource); - - // Continue if parent was already determined as to be ignored - if (ignoredPaths[parent.toString()]) { - continue; - } - - // Compute if parent is visible and added file not yet part of it - const parentStat = this.explorerService.findClosest(parent); - if (parentStat && parentStat.isDirectoryResolved && !this.explorerService.findClosest(change.resource)) { - return true; - } - - // Keep track of path that can be ignored for faster lookup - if (!parentStat || !parentStat.isDirectoryResolved) { - ignoredPaths[parent.toString()] = true; - } - } - } - - // Handle deleted files/folders - const deleted = e.getDeleted(); - if (deleted.length) { - - // Check deleted: Refresh if deleted file/folder part of resolved root - for (let j = 0; j < deleted.length; j++) { - const del = deleted[j]; - - if (this.explorerService.findClosest(del.resource)) { - return true; - } - } - } - - // Handle updated files/folders if we sort by modified - if (this.sortOrder === SortOrderConfiguration.MODIFIED) { - const updated = e.getUpdated(); - - // Check updated: Refresh if updated file/folder part of resolved root - for (let j = 0; j < updated.length; j++) { - const upd = updated[j]; - - if (this.explorerService.findClosest(upd.resource)) { - return true; - } - } - } - - return false; - } - - private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { - return new FileChangesEvent(e.changes.filter(change => { - if (change.type === FileChangeType.UPDATED && this.sortOrder !== SortOrderConfiguration.MODIFIED) { - return false; // we only are about updated if we sort by modified time - } - - if (!this.contextService.isInsideWorkspace(change.resource)) { - return false; // exclude changes for resources outside of workspace - } - - if (this.fileEventsFilter.matches(change.resource)) { - return false; // excluded via files.exclude setting - } - - return true; - })); - } - private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void { if (this.isVisible() && !this.isDisposed) { this.explorerRefreshDelayer.trigger(() => { 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 6a9ba0ff1ce..8a95f09638c 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -31,8 +31,8 @@ import { normalize, join, nativeSep } from 'vs/base/common/paths'; import { rtrim } from 'vs/base/common/strings'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; -import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerService'; import { IAction } from 'vs/base/common/actions'; +import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; export class ExplorerDelegate implements IListVirtualDelegate { diff --git a/src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts similarity index 99% rename from src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts rename to src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts index 3b03d9a38b2..b4133dfedd2 100644 --- a/src/vs/workbench/parts/files/test/electron-browser/explorerService.test.ts +++ b/src/vs/workbench/parts/files/test/electron-browser/explorerModel.test.ts @@ -9,7 +9,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/paths'; import { validateFileName } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerService'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem { return new ExplorerItem(toResource(path), undefined, false, false, isFolder, name, mtime); From 3b11ac46d6930320bbffca9a08677fa0d5422eb8 Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Tue, 18 Dec 2018 12:57:33 -0800 Subject: [PATCH 0027/1066] 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 33cafe77ccf4684abc3485b7158298f9bcebba8e Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 10:23:58 +0100 Subject: [PATCH 0028/1066] explorerService.select --- .../files/browser/editors/textFileEditor.ts | 9 +-- src/vs/workbench/parts/files/common/files.ts | 16 ++--- .../files/electron-browser/explorerService.ts | 36 +++++++++- .../files/electron-browser/explorerViewlet.ts | 6 +- .../files/electron-browser/fileCommands.ts | 5 +- .../electron-browser/views/explorerView.ts | 69 +++---------------- 6 files changed, 62 insertions(+), 79 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index ba0a6eac16e..0dc80557ad9 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -8,7 +8,7 @@ import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as types from 'vs/base/common/types'; import * as paths from 'vs/base/common/paths'; import { Action } from 'vs/base/common/actions'; -import { VIEWLET_ID, IExplorerViewlet, TEXT_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; +import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; @@ -54,7 +54,8 @@ export class TextFileEditor extends BaseTextEditor { @ITextFileService textFileService: ITextFileService, @IWindowsService private windowsService: IWindowsService, @IPreferencesService private preferencesService: IPreferencesService, - @IWindowService windowService: IWindowService + @IWindowService windowService: IWindowService, + @IExplorerService private explorerService: IExplorerService ) { super(TextFileEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService); @@ -218,8 +219,8 @@ export class TextFileEditor extends BaseTextEditor { // Best we can do is to reveal the folder in the explorer if (this.contextService.isInsideWorkspace(input.getResource())) { - this.viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { - return (viewlet as IExplorerViewlet).getExplorerView().select(input.getResource(), true); + this.viewletService.openViewlet(VIEWLET_ID, true).then(() => { + this.explorerService.select(input.getResource(), true); }); } }); diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 3f63a17f4d7..acc187e0581 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -15,7 +15,6 @@ import { Event } from 'vs/base/common/event'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IViewlet } from 'vs/workbench/common/viewlet'; import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys'; import { Registry } from 'vs/platform/registry/common/platform'; import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; @@ -34,14 +33,6 @@ export const VIEWLET_ID = 'workbench.view.explorer'; */ export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); -export interface IExplorerViewlet extends IViewlet { - getExplorerView(): IExplorerView; -} - -export interface IExplorerView { - select(resource: URI, reveal?: boolean): void; -} - export interface IEditableData { validationMessage: (value: string) => string; action: IAction; @@ -52,10 +43,17 @@ export interface IExplorerService { readonly roots: ExplorerItem[]; readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; + readonly onDidSelectItem: Event<{ item: ExplorerItem, reveal: boolean }>; setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; findClosest(resource: URI): ExplorerItem | null; + + /** + * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to + * resolve the path from the disk in case the explorer is not yet expanded to the file yet. + */ + select(resource: URI, reveal?: boolean): void; } export const IExplorerService = createDecorator('explorerService'); diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 95dc09812f7..2a2c48664c8 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -9,13 +9,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IExplorerService, IEditableData, IFilesConfiguration, SortOrder, SortOrderConfiguration } from 'vs/workbench/parts/files/common/files'; import { ExplorerItem, ExplorerModel } from 'vs/workbench/parts/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; -import { FileOperationEvent, FileOperation, IFileStat, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType } from 'vs/platform/files/common/files'; +import { FileOperationEvent, FileOperation, IFileStat, IFileService, FileChangesEvent, FILES_EXCLUDE_CONFIG, FileChangeType, IResolveFileOptions } from 'vs/platform/files/common/files'; import { dirname } from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IExpression } from 'vs/base/common/glob'; +import { INotificationService } from 'vs/platform/notification/common/notification'; function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { const scope = root ? { resource: root } : void 0; @@ -31,6 +32,7 @@ export class ExplorerService implements IExplorerService { private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); + private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); private disposables: IDisposable[] = []; private editableStats = new Map(); private sortOrder: SortOrder; @@ -39,7 +41,8 @@ export class ExplorerService implements IExplorerService { @IFileService private fileService: IFileService, @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @INotificationService private notificationService: INotificationService, ) { } get roots(): ExplorerItem[] { @@ -54,6 +57,10 @@ export class ExplorerService implements IExplorerService { return this._onDidChangeEditable.event; } + get onDidSelectItem(): Event<{ item: ExplorerItem, reveal: boolean }> { + return this._onDidSelectItem.event; + } + // Memoized locals @memoize private get fileEventsFilter(): ResourceGlobMatcher { const fileEventsFilter = this.instantiationService.createInstance( @@ -89,6 +96,31 @@ export class ExplorerService implements IExplorerService { return this.editableStats.get(stat); } + select(resource: URI, reveal?: boolean): void { + const fileStat = this.findClosest(resource); + if (fileStat) { + this._onDidSelectItem.fire({ item: fileStat, reveal }); + return; + } + + // Stat needs to be resolved first and then revealed + const options: IResolveFileOptions = { resolveTo: [resource] }; + const workspaceFolder = this.contextService.getWorkspaceFolder(resource); + const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; + this.fileService.resolveFile(rootUri, options).then(stat => { + + // Convert to model + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + const modelStat = ExplorerItem.create(stat, root, options.resolveTo); + // Update Input with disk Stat + ExplorerItem.mergeLocalWithDisk(modelStat, root); + + // Select and Reveal + this._onDidSelectItem.fire({ item: root.find(resource), reveal }); + }, e => { this.notificationService.error(e); }); + } + + private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; if (this.sortOrder !== configSortOrder) { diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index fb16a1dea62..147803ba40b 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/explorerviewlet'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; -import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition, IExplorerViewlet, VIEW_CONTAINER } from 'vs/workbench/parts/files/common/files'; +import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, OpenEditorsVisibleCondition, VIEW_CONTAINER } from 'vs/workbench/parts/files/common/files'; import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; @@ -144,7 +144,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor } } -export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerViewlet { +export class ExplorerViewlet extends ViewContainerViewlet { private static readonly EXPLORER_VIEWS_STATE = 'workbench.explorer.views.state'; @@ -236,7 +236,7 @@ export class ExplorerViewlet extends ViewContainerViewlet implements IExplorerVi } focus(): void { - const explorerView = this.getExplorerView(); + const explorerView = this.getView(ExplorerView.ID); if (explorerView && explorerView.isExpanded()) { explorerView.focus(); } else { diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index 7d6505f3a82..410dc66cabb 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -11,7 +11,7 @@ import { IWindowsService, IWindowService } from 'vs/platform/windows/common/wind import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ExplorerFocusCondition, FileOnDiskContentProvider, VIEWLET_ID } from 'vs/workbench/parts/files/common/files'; +import { ExplorerFocusCondition, FileOnDiskContentProvider, VIEWLET_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; @@ -463,6 +463,7 @@ CommandsRegistry.registerCommand({ handler: (accessor, resource: URI | object) => { const viewletService = accessor.get(IViewletService); const contextService = accessor.get(IWorkspaceContextService); + const explorerService = accessor.get(IExplorerService); const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); viewletService.openViewlet(VIEWLET_ID, false).then((viewlet: ExplorerViewlet) => { @@ -471,7 +472,7 @@ CommandsRegistry.registerCommand({ const explorerView = viewlet.getExplorerView(); if (explorerView) { explorerView.setExpanded(true); - explorerView.select(uri, true); + explorerService.select(uri, true); } } else { const openEditorsView = viewlet.getOpenEditorsView(); 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 9213168c8b8..49d3103fa11 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -11,8 +11,8 @@ import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { Action, IAction } from 'vs/base/common/actions'; import { memoize } from 'vs/base/common/decorators'; -import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, IExplorerView, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; -import { IResolveFileOptions, IFileService } from 'vs/platform/files/common/files'; +import { IFilesConfiguration, ExplorerFolderContext, FilesExplorerFocusedContext, ExplorerFocusedContext, ExplorerRootContext, ExplorerResourceReadonlyContext, IExplorerService } from 'vs/workbench/parts/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { RefreshViewExplorerAction, NewFolderAction, NewFileAction, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { toResource } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; @@ -33,7 +33,6 @@ import { IDecorationsService } from 'vs/workbench/services/decorations/browser/d import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; -import { INotificationService } from 'vs/platform/notification/common/notification'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -47,8 +46,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; -export class ExplorerView extends ViewletPanel implements IExplorerView { - +export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes @@ -71,7 +69,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { constructor( options: IViewletPanelOptions, - @INotificationService private notificationService: INotificationService, @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService private instantiationService: IInstantiationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, @@ -190,6 +187,9 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { this._onDidChangeTitleArea.fire(); this.refreshFromEvent(); })); + + this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); + // TODO@Isidor plug in other explorer service events } getActions(): IAction[] { @@ -219,7 +219,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { const selection = this.hasSingleSelection(activeFile); if (!selection) { - this.select(activeFile); + this.explorerService.select(activeFile); } clearSelection = false; @@ -290,7 +290,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { const activeFile = this.getActiveFile(); if (activeFile) { refreshPromise.then(() => { - this.select(activeFile); + this.explorerService.select(activeFile); }); return; } @@ -508,7 +508,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { return this.doRefresh().then(() => { if (resourceToFocus) { - return this.select(resourceToFocus, true); + return this.explorerService.select(resourceToFocus, true); } return Promise.resolve(void 0); @@ -624,55 +624,6 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { } } - /** - * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to - * resolve the path from the disk in case the explorer is not yet expanded to the file yet. - */ - select(resource: URI, reveal: boolean = this.autoReveal): void { - - // Require valid path - if (!resource) { - return; - } - - // If path already selected, just reveal and return - const selection = this.hasSingleSelection(resource); - if (selection) { - if (reveal) { - this.tree.reveal(selection, 0.5); - } - - return; - } - - if (!this.isCreated) { - return; - } - - const fileStat = this.explorerService.findClosest(resource); - if (fileStat) { - this.doSelect(fileStat, reveal); - return; - } - - // Stat needs to be resolved first and then revealed - const options: IResolveFileOptions = { resolveTo: [resource] }; - const workspaceFolder = this.contextService.getWorkspaceFolder(resource); - const rootUri = workspaceFolder ? workspaceFolder.uri : this.explorerService.roots[0].resource; - this.fileService.resolveFile(rootUri, options).then(stat => { - - // Convert to model - const root = this.explorerService.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); - const modelStat = ExplorerItem.create(stat, root, options.resolveTo); - // Update Input with disk Stat - ExplorerItem.mergeLocalWithDisk(modelStat, root); - - // Select and Reveal - return this.tree.refresh(root).then(() => this.doSelect(root.find(resource), reveal)); - - }, e => { this.notificationService.error(e); }); - } - private hasSingleSelection(resource: URI): ExplorerItem { const currentSelection: ExplorerItem[] = this.tree.getSelection(); return currentSelection.length === 1 && currentSelection[0].resource.toString() === resource.toString() @@ -680,7 +631,7 @@ export class ExplorerView extends ViewletPanel implements IExplorerView { : undefined; } - private doSelect(fileStat: ExplorerItem, reveal: boolean): Promise { + private onSelectItem(fileStat: ExplorerItem, reveal = this.autoReveal): Promise { if (!fileStat) { return Promise.resolve(void 0); } From 3eabca14342821d121dc6b51dae9bd4ae0427119 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 12:06:00 +0100 Subject: [PATCH 0029/1066] explorer: simplify explorer view, it no longer resolves stats --- src/vs/workbench/parts/files/common/files.ts | 4 +- .../files/electron-browser/explorerService.ts | 13 +- .../files/electron-browser/fileActions.ts | 11 +- .../files/electron-browser/fileCommands.ts | 3 +- .../electron-browser/views/explorerView.ts | 217 ++++-------------- .../electron-browser/views/explorerViewer.ts | 19 +- 6 files changed, 66 insertions(+), 201 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index acc187e0581..45787cca779 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -41,7 +41,7 @@ export interface IEditableData { export interface IExplorerService { _serviceBrand: any; readonly roots: ExplorerItem[]; - readonly onDidChangeItem: Event; + readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; readonly onDidSelectItem: Event<{ item: ExplorerItem, reveal: boolean }>; @@ -53,7 +53,7 @@ export interface IExplorerService { * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to * resolve the path from the disk in case the explorer is not yet expanded to the file yet. */ - select(resource: URI, reveal?: boolean): void; + select(resource: URI, reveal?: boolean): Promise; } export const IExplorerService = createDecorator('explorerService'); diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 2a2c48664c8..9c48a935e2c 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -30,7 +30,7 @@ export class ExplorerService implements IExplorerService { private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first - private _onDidChangeItem = new Emitter(); + private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); private disposables: IDisposable[] = []; @@ -49,7 +49,7 @@ export class ExplorerService implements IExplorerService { return this.model.roots; } - get onDidChangeItem(): Event { + get onDidChangeItem(): Event { return this._onDidChangeItem.event; } @@ -79,6 +79,7 @@ export class ExplorerService implements IExplorerService { this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); + this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire())); return model; } @@ -96,18 +97,18 @@ export class ExplorerService implements IExplorerService { return this.editableStats.get(stat); } - select(resource: URI, reveal?: boolean): void { + select(resource: URI, reveal?: boolean): Promise { const fileStat = this.findClosest(resource); if (fileStat) { this._onDidSelectItem.fire({ item: fileStat, reveal }); - return; + return Promise.resolve(void 0); } // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; - this.fileService.resolveFile(rootUri, options).then(stat => { + return this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); @@ -120,7 +121,6 @@ export class ExplorerService implements IExplorerService { }, e => { this.notificationService.error(e); }); } - private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; if (this.sortOrder !== configSortOrder) { @@ -130,6 +130,7 @@ export class ExplorerService implements IExplorerService { } // File events + private onFileOperation(e: FileOperationEvent): void { // Add if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 4246f210ffb..7764b2cbbf2 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -20,7 +20,6 @@ import { VIEWLET_ID, IExplorerService } from 'vs/workbench/parts/files/common/fi import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService, IFileStat, AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; -import { ExplorerView } from 'vs/workbench/parts/files/electron-browser/views/explorerView'; import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; @@ -985,14 +984,6 @@ export class GlobalCompareResourcesAction extends Action { } } -// Refresh Explorer Viewer -export class RefreshViewExplorerAction extends Action { - - constructor(explorerView: ExplorerView, clazz: string) { - super('workbench.files.action.refreshFilesExplorer', nls.localize('refresh', "Refresh"), clazz, true, (context: any) => explorerView.refresh()); - } -} - export class ToggleAutoSaveAction extends Action { public static readonly ID = 'workbench.action.toggleAutoSave'; public static readonly LABEL = nls.localize('toggleAutoSave', "Toggle Auto Save"); @@ -1215,7 +1206,7 @@ export class RefreshExplorerView extends Action { label: string, @IViewletService private viewletService: IViewletService ) { - super(id, label); + super(id, label, 'explorer-action refresh-explorer'); } public run(): Promise { diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index 410dc66cabb..a376b7cefc0 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -39,6 +39,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { ILabelService } from 'vs/platform/label/common/label'; +import { onUnexpectedError } from 'vs/base/common/errors'; // Commands @@ -472,7 +473,7 @@ CommandsRegistry.registerCommand({ const explorerView = viewlet.getExplorerView(); if (explorerView) { explorerView.setExpanded(true); - explorerService.select(uri, true); + explorerService.select(uri, true).then(undefined, onUnexpectedError); } } else { const openEditorsView = viewlet.getOpenEditorsView(); 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 49d3103fa11..af8bac74d02 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -7,13 +7,11 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; import { ThrottledDelayer, sequence, ignoreErrors } from 'vs/base/common/async'; -import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; 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 { IFileService } from 'vs/platform/files/common/files'; -import { RefreshViewExplorerAction, NewFolderAction, NewFileAction, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; +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'; import * as DOM from 'vs/base/browser/dom'; @@ -45,14 +43,14 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; - private isCreated: boolean; private explorerRefreshDelayer: ThrottledDelayer; @@ -61,7 +59,8 @@ export class ExplorerView extends ViewletPanel { private readonlyContext: IContextKey; private rootContext: IContextKey; - private shouldRefresh: boolean; + // Refresh is needed on the initial explorer open + private shouldRefresh = true; private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; @@ -74,7 +73,6 @@ export class ExplorerView extends ViewletPanel { @IWorkspaceContextService private contextService: IWorkspaceContextService, @IProgressService private progressService: IProgressService, @IEditorService private editorService: IEditorService, - @IFileService private fileService: IFileService, @IPartService private partService: IPartService, @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService private contextKeyService: IContextKeyService, @@ -159,17 +157,11 @@ export class ExplorerView extends ViewletPanel { const configuration = this.configurationService.getValue(); this.onConfigurationUpdated(configuration); - // Load and Fill Viewer - this.doRefresh().then(() => { + // When the explorer viewer is loaded, listen to changes to the editor input + this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.revealActiveFile())); - // When the explorer viewer is loaded, listen to changes to the editor input - this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.revealActiveFile())); - - // Also handle configuration updates - this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); - - this.revealActiveFile(); - }); + // Also handle configuration updates + this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); } renderBody(container: HTMLElement): void { @@ -180,16 +172,16 @@ export class ExplorerView extends ViewletPanel { this.toolbar.setActions(this.getActions(), this.getSecondaryActions())(); } - this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.refreshFromEvent(e.added))); - this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.refreshFromEvent())); - this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this.refreshFromEvent())); + this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.setTreeInput(e.added))); + this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.setTreeInput())); this.disposables.push(this.labelService.onDidRegisterFormatter(() => { this._onDidChangeTitleArea.fire(); this.refreshFromEvent(); })); + this.disposables.push(this.explorerService.onDidChangeItem(e => this.refreshFromEvent(e))); + this.disposables.push(this.explorerService.onDidChangeEditable(e => this.refresh(e.parent))); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); - // TODO@Isidor plug in other explorer service events } getActions(): IAction[] { @@ -197,7 +189,7 @@ export class ExplorerView extends ViewletPanel { actions.push(this.instantiationService.createInstance(NewFileAction, null)); actions.push(this.instantiationService.createInstance(NewFolderAction, null)); - actions.push(this.instantiationService.createInstance(RefreshViewExplorerAction, this, 'explorer-action refresh-explorer')); + actions.push(this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL)); actions.push(this.instantiationService.createInstance(CollapseAction2, this.tree, true, 'explorer-action collapse-explorer')); return actions; @@ -219,7 +211,7 @@ export class ExplorerView extends ViewletPanel { if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { const selection = this.hasSingleSelection(activeFile); if (!selection) { - this.explorerService.select(activeFile); + this.explorerService.select(activeFile).then(undefined, onUnexpectedError); } clearSelection = false; @@ -270,50 +262,12 @@ export class ExplorerView extends ViewletPanel { setVisible(visible: boolean): void { super.setVisible(visible); - - // Show if (visible) { - DOM.show(this.tree.getHTMLElement()); // If a refresh was requested and we are now visible, run it - let refreshPromise = Promise.resolve(null); if (this.shouldRefresh) { - refreshPromise = this.doRefresh(); - this.shouldRefresh = false; // Reset flag + this.shouldRefresh = false; + this.setTreeInput().then(undefined, onUnexpectedError); } - - if (!this.autoReveal) { - return; // do not react to setVisible call if autoReveal === false - } - - // Always select the current navigated file in explorer if input is file editor input - // unless autoReveal is set to false - const activeFile = this.getActiveFile(); - if (activeFile) { - refreshPromise.then(() => { - this.explorerService.select(activeFile); - }); - return; - } - - // Return now if the workbench has not yet been restored - in this case the workbench takes care of restoring last used editors - if (!this.partService.isRestored()) { - return; - } - - // Otherwise restore last used file: By lastActiveFileResource - const focusedElements = this.tree.getFocus(); - if (focusedElements && focusedElements.length) { - this.editorService.openEditor({ resource: focusedElements[0].resource, options: { revealIfVisible: true } }); - return; - } - - // Otherwise restore last used file: By Explorer selection - refreshPromise.then(() => { - this.openFocusedElement(); - }); - } else { - // make sure the tree goes out of the tabindex world by hiding it - DOM.hide(this.tree.getHTMLElement()); } } @@ -361,7 +315,6 @@ export class ExplorerView extends ViewletPanel { FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); ExplorerFocusedContext.bindTo(this.tree.contextKeyService); - // Update resource context based on focused element this.disposables.push(this.tree.onDidChangeFocus(e => { const stat = e.elements && e.elements.length ? e.elements[0] : undefined; @@ -433,20 +386,14 @@ export class ExplorerView extends ViewletPanel { // Refresh viewer as needed if this originates from a config event if (event && needsRefresh) { - this.doRefresh(); + this.refresh(); } } - private refreshFromEvent(newRoots: IWorkspaceFolder[] = []): void { + private refreshFromEvent(explorerItem?: ExplorerItem): void { if (this.isVisible() && !this.isDisposed) { this.explorerRefreshDelayer.trigger(() => { - return this.doRefresh().then(() => { - if (newRoots.length === 1) { - return this.tree.reveal(this.explorerService.findClosest(newRoots[0].uri), 0.5); - } - - return undefined; - }); + return this.refresh(explorerItem); }); } else { this.shouldRefresh = true; @@ -486,118 +433,50 @@ export class ExplorerView extends ViewletPanel { /** * Refresh the contents of the explorer to get up to date data from the disk about the file structure. */ - refresh(): Promise { + refresh(item?: ExplorerItem): Promise { if (!this.tree) { return Promise.resolve(void 0); } + const toRefresh = item || this.tree.getInput(); - // Focus - this.tree.domFocus(); - - // Find resource to focus from active editor input if set - let resourceToFocus: URI; - if (this.autoReveal) { - resourceToFocus = this.getActiveFile(); - if (!resourceToFocus) { - const selection = this.tree.getSelection(); - if (selection && selection.length === 1) { - resourceToFocus = selection[0].resource; - } - } - } - - return this.doRefresh().then(() => { - if (resourceToFocus) { - return this.explorerService.select(resourceToFocus, true); - } + return this.tree.refresh(toRefresh, true); + } + private setTreeInput(newRoots?: IWorkspaceFolder[]): Promise { + if (!this.isVisible()) { + this.shouldRefresh = true; return Promise.resolve(void 0); - }); - } + } - private doRefresh(): Promise { - const targetsToResolve = this.explorerService.roots.map(root => ({ root, resource: root.resource, options: { resolveTo: [] } })); + perf.mark('willResolveExplorer'); + const roots = this.explorerService.roots; + let input: ExplorerItem | ExplorerItem[] = roots[0]; + if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || roots[0].isError) { + // Display roots only when multi folder workspace + input = roots; + } - // First time refresh: Receive target through active editor input or selection and also include settings from previous session - if (!this.isCreated) { - const activeFile = this.getActiveFile(); - if (activeFile) { - const workspaceFolder = this.contextService.getWorkspaceFolder(activeFile); - if (workspaceFolder) { - const found = targetsToResolve.filter(t => t.root.resource.toString() === workspaceFolder.uri.toString()).pop(); - found.options.resolveTo.push(activeFile); + const promise = this.tree.setInput(input).then(() => { + let expandPromise = Promise.resolve(void 0); + if (newRoots && newRoots.length) { + expandPromise = Promise.all(newRoots.map(workspaceFolder => this.tree.expand(this.explorerService.findClosest(workspaceFolder.uri)))); + } + + // Find resource to focus from active editor input if set + if (this.autoReveal) { + const resourceToFocus = this.getActiveFile(); + if (resourceToFocus) { + return expandPromise.then(() => this.explorerService.select(resourceToFocus, true)); } } - } - // Subsequent refresh: Receive targets through expanded folders in tree - else { - targetsToResolve.forEach(t => { - this.getResolvedDirectories(t.root, t.options.resolveTo); - }); - } + return expandPromise; + }).then(() => perf.mark('didResolveExplorer')); - const promise = this.resolveRoots(targetsToResolve).then(result => { - this.isCreated = true; - this.decorationProvider.changed(targetsToResolve.map(t => t.root.resource)); - return result; - }); this.progressService.showWhile(promise, this.partService.isRestored() ? 800 : 1200 /* less ugly initial startup */); - return promise; } - private resolveRoots(targetsToResolve: { root: ExplorerItem, resource: URI, options: { resolveTo: any[] } }[]): Promise { - - if (!this.isCreated) { - perf.mark('willResolveExplorer'); - } - - const errorRoot = (resource: URI, root: ExplorerItem) => { - return ExplorerItem.create({ - resource: resource, - name: paths.basename(resource.fsPath), - mtime: 0, - etag: undefined, - isDirectory: true - }, root, undefined, true); - }; - - if (targetsToResolve.every(t => t.root.resource.scheme === 'file')) { - // All the roots are local, resolve them in parallel - return this.fileService.resolveFiles(targetsToResolve).then(results => { - // Convert to model - const modelStats = results.map((result, index) => { - if (result.success && result.stat.isDirectory) { - return ExplorerItem.create(result.stat, targetsToResolve[index].root, targetsToResolve[index].options.resolveTo); - } - - return errorRoot(targetsToResolve[index].resource, targetsToResolve[index].root); - }); - // Subsequent refresh: Merge stat into our local model and refresh tree - modelStats.forEach((modelStat, index) => { - if (index < this.explorerService.roots.length) { - ExplorerItem.mergeLocalWithDisk(modelStat, this.explorerService.roots[index]); - } - }); - - return this.tree.setInput(null); - }); - } - - // There is a remote root, resolve the roots sequantally - return Promise.all(targetsToResolve.map((target, index) => this.fileService.resolveFile(target.resource, target.options) - .then(result => result.isDirectory ? ExplorerItem.create(result, target.root, target.options.resolveTo) : errorRoot(target.resource, target.root), () => errorRoot(target.resource, target.root)) - .then(modelStat => { - // Subsequent refresh: Merge stat into our local model and refresh tree - if (index < this.explorerService.roots.length) { - ExplorerItem.mergeLocalWithDisk(modelStat, this.explorerService.roots[index]); - } - - return this.tree.setInput(null); - }))); - } - /** * Given a stat, fills an array of path that make all folders below the stat that are resolved directories. */ 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 8a95f09638c..e05f559fccf 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -47,29 +47,22 @@ export class ExplorerDelegate implements IListVirtualDelegate { } } -export class ExplorerDataSource implements IAsyncDataSource { +export class ExplorerDataSource implements IAsyncDataSource { constructor( - @IExplorerService private explorerService: IExplorerService, @IProgressService private progressService: IProgressService, @INotificationService private notificationService: INotificationService, @IFileService private fileService: IFileService, @IPartService private partService: IPartService, - @IWorkspaceContextService private contextService: IWorkspaceContextService ) { } - hasChildren(element: ExplorerItem | null): boolean { - return element === null || element.isDirectory; + hasChildren(element: ExplorerItem | ExplorerItem[]): boolean { + return Array.isArray(element) || element.isDirectory; } - getChildren(element: ExplorerItem | null): Promise { - if (element === null) { - const roots = this.explorerService.roots; - if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || roots[0].isError) { - // Display roots only when multi folder workspace - return Promise.resolve(roots); - } - element = roots[0]; + getChildren(element: ExplorerItem | ExplorerItem[]): Promise { + if (Array.isArray(element)) { + return Promise.resolve(element); } // Return early if stat is already resolved From 2a2616b149f6b29e7e2ac73ad499aaa558b0e16d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 12:20:10 +0100 Subject: [PATCH 0030/1066] explorer: remove unnnecessery methods --- .../electron-browser/views/explorerView.ts | 179 +++--------------- 1 file changed, 30 insertions(+), 149 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 af8bac74d02..12781a7a96d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; import { ThrottledDelayer, sequence, ignoreErrors } from 'vs/base/common/async'; -import * as resources from 'vs/base/common/resources'; 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'; @@ -26,11 +25,9 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { isLinux } from 'vs/base/common/platform'; import { IDecorationsService } from 'vs/workbench/services/decorations/browser/decorations'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; -import { Schemas } from 'vs/base/common/network'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -64,7 +61,6 @@ export class ExplorerView extends ViewletPanel { private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; - private isDisposed = false; constructor( options: IViewletPanelOptions, @@ -102,29 +98,6 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.resourceContext); } - protected renderHeader(container: HTMLElement): void { - super.renderHeader(container); - - // Expand on drag over - this.dragHandler = new DelayedDragHandler(container, () => this.setExpanded(true)); - - const titleElement = container.querySelector('.title') as HTMLElement; - const setHeader = () => { - const workspace = this.contextService.getWorkspace(); - const title = workspace.folders.map(folder => folder.name).join(); - titleElement.textContent = this.name; - titleElement.title = title; - }; - - this.disposables.push(this.contextService.onDidChangeWorkspaceName(setHeader)); - this.disposables.push(this.labelService.onDidRegisterFormatter(setHeader)); - setHeader(); - } - - protected layoutBody(size: number): void { - this.tree.layout(size); - } - get name(): string { return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); } @@ -150,18 +123,27 @@ export class ExplorerView extends ViewletPanel { // Split view methods - render(): void { - super.render(); + protected renderHeader(container: HTMLElement): void { + super.renderHeader(container); - // Update configuration - const configuration = this.configurationService.getValue(); - this.onConfigurationUpdated(configuration); + // Expand on drag over + this.dragHandler = new DelayedDragHandler(container, () => this.setExpanded(true)); - // When the explorer viewer is loaded, listen to changes to the editor input - this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.revealActiveFile())); + const titleElement = container.querySelector('.title') as HTMLElement; + const setHeader = () => { + const workspace = this.contextService.getWorkspace(); + const title = workspace.folders.map(folder => folder.name).join(); + titleElement.textContent = this.name; + titleElement.title = title; + }; - // Also handle configuration updates - this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); + this.disposables.push(this.contextService.onDidChangeWorkspaceName(setHeader)); + this.disposables.push(this.labelService.onDidRegisterFormatter(setHeader)); + setHeader(); + } + + protected layoutBody(size: number): void { + this.tree.layout(size); } renderBody(container: HTMLElement): void { @@ -182,6 +164,16 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.explorerService.onDidChangeItem(e => this.refreshFromEvent(e))); this.disposables.push(this.explorerService.onDidChangeEditable(e => this.refresh(e.parent))); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); + + // Update configuration + const configuration = this.configurationService.getValue(); + this.onConfigurationUpdated(configuration); + + // When the explorer viewer is loaded, listen to changes to the editor input + this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.explorerService.select(this.getActiveFile()))); + + // Also handle configuration updates + this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); } getActions(): IAction[] { @@ -195,71 +187,6 @@ export class ExplorerView extends ViewletPanel { return actions; } - private revealActiveFile(): void { - if (!this.autoReveal) { - return; // do not touch selection or focus if autoReveal === false - } - - let clearSelection = true; - let clearFocus = false; - - // Handle files - const activeFile = this.getActiveFile(); - if (activeFile) { - - // Select file if input is inside workspace - if (this.isVisible() && !this.isDisposed && this.contextService.isInsideWorkspace(activeFile)) { - const selection = this.hasSingleSelection(activeFile); - if (!selection) { - this.explorerService.select(activeFile).then(undefined, onUnexpectedError); - } - - clearSelection = false; - } - } - - // Handle closed or untitled file (convince explorer to not reopen any file when getting visible) - const activeInput = this.editorService.activeEditor; - if (!activeInput || toResource(activeInput, { supportSideBySide: true, filter: Schemas.untitled })) { - clearFocus = true; - } - - // Otherwise clear - if (clearSelection) { - this.tree.setSelection([]); - } - - if (clearFocus) { - this.tree.setFocus([]); - } - } - - focus(): void { - super.focus(); - - let keepFocus = false; - - // Make sure the current selected element is revealed - if (this.tree) { - if (this.autoReveal) { - const selection = this.tree.getSelection(); - if (selection.length > 0) { - this.tree.reveal(selection[0], 0.5); - } - } - - // Pass Focus to Viewer - this.tree.domFocus(); - keepFocus = true; - } - - // Open the focused element in the editor if there is currently no file opened - const activeFile = this.getActiveFile(); - if (!activeFile) { - this.openFocusedElement(keepFocus); - } - } - setVisible(visible: boolean): void { super.setVisible(visible); if (visible) { @@ -271,14 +198,6 @@ export class ExplorerView extends ViewletPanel { } } - private openFocusedElement(preserveFocus?: boolean): void { - const focusedElements = this.tree.getFocus(); - const stat = focusedElements && focusedElements.length ? focusedElements[0] : undefined; - if (stat && !stat.isDirectory) { - this.editorService.openEditor({ resource: stat.resource, options: { preserveFocus, revealIfVisible: true } }); - } - } - private getActiveFile(): URI { const input = this.editorService.activeEditor; @@ -367,10 +286,6 @@ export class ExplorerView extends ViewletPanel { // React on events private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { - if (this.isDisposed) { - return; // guard against possible race condition when config change causes recreate of views - } - this.autoReveal = configuration && configuration.explorer && configuration.explorer.autoReveal; // Push down config updates to components of viewer @@ -391,7 +306,7 @@ export class ExplorerView extends ViewletPanel { } private refreshFromEvent(explorerItem?: ExplorerItem): void { - if (this.isVisible() && !this.isDisposed) { + if (this.isVisible()) { this.explorerRefreshDelayer.trigger(() => { return this.refresh(explorerItem); }); @@ -477,41 +392,8 @@ export class ExplorerView extends ViewletPanel { return promise; } - /** - * Given a stat, fills an array of path that make all folders below the stat that are resolved directories. - */ - private getResolvedDirectories(stat: ExplorerItem, resolvedDirectories: URI[]): void { - if (stat.isDirectoryResolved) { - if (!stat.isRoot) { - - // Drop those path which are parents of the current one - for (let i = resolvedDirectories.length - 1; i >= 0; i--) { - const resource = resolvedDirectories[i]; - if (resources.isEqualOrParent(stat.resource, resource, !isLinux /* ignorecase */)) { - resolvedDirectories.splice(i); - } - } - - // Add to the list of path to resolve - resolvedDirectories.push(stat.resource); - } - - // Recurse into children - stat.getChildrenArray().forEach(child => { - this.getResolvedDirectories(child, resolvedDirectories); - }); - } - } - - private hasSingleSelection(resource: URI): ExplorerItem { - const currentSelection: ExplorerItem[] = this.tree.getSelection(); - return currentSelection.length === 1 && currentSelection[0].resource.toString() === resource.toString() - ? currentSelection[0] - : undefined; - } - private onSelectItem(fileStat: ExplorerItem, reveal = this.autoReveal): Promise { - if (!fileStat) { + if (!fileStat || !this.isVisible()) { return Promise.resolve(void 0); } @@ -541,7 +423,6 @@ export class ExplorerView extends ViewletPanel { } dispose(): void { - this.isDisposed = true; if (this.dragHandler) { this.dragHandler.dispose(); } From 5c3daa440c5a38499e43f9450652dd646a72b97f Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 12:21:53 +0100 Subject: [PATCH 0031/1066] use undefined in explorer, not null --- .../parts/files/common/explorerModel.ts | 20 +++++++++---------- .../files/electron-browser/explorerService.ts | 2 +- .../electron-browser/views/explorerView.ts | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index b98b17a1019..36e4c2a12d7 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -42,9 +42,9 @@ export class ExplorerModel implements IDisposable { /** * Returns a FileStat that matches the passed resource. * In case multiple FileStat are matching the resource (same folder opened multiple times) returns the FileStat that has the closest root. - * Will return null in case the FileStat does not exist. + * Will return undefined in case the FileStat does not exist. */ - findClosest(resource: URI): ExplorerItem | null { + findClosest(resource: URI): ExplorerItem | undefined { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { const root = this.roots.filter(r => r.resource.toString() === folder.uri.toString()).pop(); @@ -53,7 +53,7 @@ export class ExplorerModel implements IDisposable { } } - return null; + return undefined; } dispose(): void { @@ -330,9 +330,9 @@ export class ExplorerItem { /** * Returns a child stat from this stat that matches with the provided path. - * Will return "null" in case the child does not exist. + * Will return "undefined" in case the child does not exist. */ - find(resource: URI): ExplorerItem | null { + find(resource: URI): ExplorerItem | undefined { // Return if path found // For performance reasons try to do the comparison as fast as possible if (resource && this.resource.scheme === resource.scheme && equalsIgnoreCase(this.resource.authority, resource.authority) && @@ -340,10 +340,10 @@ export class ExplorerItem { return this.findByPath(rtrim(resource.path, paths.sep), this.resource.path.length); } - return null; //Unable to find + return undefined; //Unable to find } - private findByPath(path: string, index: number): ExplorerItem | null { + private findByPath(path: string, index: number): ExplorerItem | undefined { if (paths.isEqual(rtrim(this.resource.path, paths.sep), path, !isLinux)) { return this; } @@ -370,7 +370,7 @@ export class ExplorerItem { } } - return null; + return undefined; } } @@ -423,8 +423,8 @@ export class NewStatPlaceholder extends ExplorerItem { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - find(resource: URI): ExplorerItem | null { - return null; + find(resource: URI): ExplorerItem | undefined { + return undefined; } static addNewStatPlaceholder(parent: ExplorerItem, isDirectory: boolean): NewStatPlaceholder { diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 9c48a935e2c..69fa9cf4aee 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -143,7 +143,7 @@ export class ExplorerService implements IExplorerService { // Add the new file to its parent (Model) parents.forEach(p => { // We have to check if the parent is resolved #29177 - const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(null) : this.fileService.resolveFile(p.resource); + const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource); thenable.then(stat => { if (stat) { const modelStat = ExplorerItem.create(stat, p.root); 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 12781a7a96d..1e29f06b1aa 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -179,8 +179,8 @@ export class ExplorerView extends ViewletPanel { getActions(): IAction[] { const actions: Action[] = []; - actions.push(this.instantiationService.createInstance(NewFileAction, null)); - actions.push(this.instantiationService.createInstance(NewFolderAction, null)); + actions.push(this.instantiationService.createInstance(NewFileAction, undefined)); + actions.push(this.instantiationService.createInstance(NewFolderAction, undefined)); actions.push(this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL)); actions.push(this.instantiationService.createInstance(CollapseAction2, this.tree, true, 'explorer-action collapse-explorer')); @@ -203,7 +203,7 @@ export class ExplorerView extends ViewletPanel { // ignore diff editor inputs (helps to get out of diffing when returning to explorer) if (input instanceof DiffEditorInput) { - return null; + return undefined; } // check for files From 2e271d6c4292e893511b8ad110c1f7f3de100da1 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 12:32:53 +0100 Subject: [PATCH 0032/1066] ExplorerItem cleanup contructor --- .../parts/files/common/explorerModel.ts | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 36e4c2a12d7..21bf9b56815 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -62,50 +62,49 @@ export class ExplorerModel implements IDisposable { } export class ExplorerItem { - public resource: URI; - private _name: string; - public mtime?: number; - public etag?: string; - private _isDirectory: boolean; - private _isSymbolicLink: boolean; - private _isReadonly: boolean; private children?: Map; - private _isError: boolean; public parent: ExplorerItem; - public isDirectoryResolved: boolean; - constructor(resource: URI, public root: ExplorerItem | undefined, isSymbolicLink?: boolean, isReadonly?: boolean, isDirectory?: boolean, name: string = resources.basenameOrAuthority(resource), mtime?: number, etag?: string, isError?: boolean) { - this.resource = resource; - this._name = name; - this.isDirectory = !!isDirectory; - this._isSymbolicLink = !!isSymbolicLink; - this._isReadonly = !!isReadonly; - this.etag = etag; - this.mtime = mtime; - this._isError = !!isError; - + constructor( + public resource: URI, + public root: ExplorerItem | undefined, + private _isSymbolicLink?: boolean, + private _isReadonly?: boolean, + private _isDirectory?: boolean, + private _name: string = resources.basenameOrAuthority(resource), + private _mtime?: number, + private _etag?: string, + private _isError?: boolean + ) { if (!this.root) { this.root = this; } - this.isDirectoryResolved = false; } get isSymbolicLink(): boolean { - return this._isSymbolicLink; + return !!this._isSymbolicLink; } get isDirectory(): boolean { - return this._isDirectory; + return !!this._isDirectory; } get isReadonly(): boolean { - return this._isReadonly; + return !!this._isReadonly; + } + + get etag(): string { + return this._etag; + } + + get mtime(): number { + return this._mtime; } get isError(): boolean { - return this._isError; + return !!this._isError; } set isDirectory(value: boolean) { @@ -189,7 +188,7 @@ export class ExplorerItem { local.resource = disk.resource; local.updateName(disk.name); local.isDirectory = disk.isDirectory; - local.mtime = disk.mtime; + local._mtime = disk.mtime; local.isDirectoryResolved = disk.isDirectoryResolved; local._isSymbolicLink = disk.isSymbolicLink; local._isReadonly = disk.isReadonly; @@ -322,7 +321,7 @@ export class ExplorerItem { // Merge a subset of Properties that can change on rename this.updateName(renamedStat.name); - this.mtime = renamedStat.mtime; + this._mtime = renamedStat.mtime; // Update Paths including children this.updateResource(true); @@ -396,7 +395,6 @@ export class NewStatPlaceholder extends ExplorerItem { this.isDirectoryResolved = false; this.isDirectory = false; - this.mtime = void 0; } getId(): string { From 287f029be104c44c54536d8e0fdc5ed5c85cfdf4 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 12:47:17 +0100 Subject: [PATCH 0033/1066] explroer: move resolving of stats to model, not viewer --- .../parts/files/common/explorerModel.ts | 29 ++++++------ .../electron-browser/views/explorerViewer.ts | 46 ++++--------------- 2 files changed, 26 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 21bf9b56815..c6a783f9911 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -8,7 +8,7 @@ import * as paths from 'vs/base/common/paths'; import * as resources from 'vs/base/common/resources'; import { ResourceMap } from 'vs/base/common/map'; import { isLinux } from 'vs/base/common/platform'; -import { IFileStat } from 'vs/platform/files/common/files'; +import { IFileStat, IFileService } from 'vs/platform/files/common/files'; import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/base/common/strings'; import { coalesce } from 'vs/base/common/arrays'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -116,7 +116,6 @@ export class ExplorerItem { this.children = undefined; } } - } get name(): string { @@ -254,20 +253,24 @@ export class ExplorerItem { return this.children.get(this.getPlatformAwareName(name)); } - /** - * Only use this method if you need all the children since it converts a map to an array - */ - getChildrenArray(): ExplorerItem[] | undefined { - if (!this.children) { - return undefined; + fetchChildren(fileService: IFileService): Promise { + let promise = Promise.resolve(undefined); + if (!this.isDirectoryResolved) { + promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { + const resolved = ExplorerItem.create(stat, this.root); + ExplorerItem.mergeLocalWithDisk(resolved, this); + this.isDirectoryResolved = true; + }); } - const items: ExplorerItem[] = []; - this.children.forEach(child => { - items.push(child); - }); + return promise.then(() => { + const items: ExplorerItem[] = []; + this.children.forEach(child => { + items.push(child); + }); - return items; + return items; + }); } getChildrenCount(): number { 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 e05f559fccf..4ee028de2e6 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -52,8 +52,8 @@ export class ExplorerDataSource implements IAsyncDataSource { + // Do not show error for roots since we already use an explorer decoration to notify user + if (!(element instanceof ExplorerItem && element.isRoot)) { + this.notificationService.error(e); + } - // Resolve children and add to fileStat for future lookup - else { - // Resolve - const promise = this.fileService.resolveFile(element.resource, { resolveSingleChildDescendants: true }).then(dirStat => { + return []; // we could not resolve any children because of an error + }); - // Convert to view model - const modelDirStat = ExplorerItem.create(dirStat, element.root); - - // Add children to folder - const children = modelDirStat.getChildrenArray(); - if (children) { - children.forEach(child => { - element.addChild(child); - }); - } - - element.isDirectoryResolved = true; - - return element.getChildrenArray(); - }, (e: any) => { - // Do not show error for roots since we already use an explorer decoration to notify user - if (!(element instanceof ExplorerItem && element.isRoot)) { - this.notificationService.error(e); - } - - return []; // we could not resolve any children because of an error - }); - - this.progressService.showWhile(promise, this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */); - - return promise; - } + this.progressService.showWhile(promise, this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */); + return promise; } } From b82a515257153b2df7c4b677a6e4efd7312f8855 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 13:02:18 +0100 Subject: [PATCH 0034/1066] explorerModel: some cleanup and fix tests --- .../parts/files/common/explorerModel.ts | 89 ++++++------------- .../electron-browser/explorerModel.test.ts | 17 +--- 2 files changed, 32 insertions(+), 74 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index c6a783f9911..83ce59db95d 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -13,6 +13,7 @@ import { rtrim, startsWithIgnoreCase, startsWith, equalsIgnoreCase } from 'vs/ba import { coalesce } from 'vs/base/common/arrays'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { memoize } from 'vs/base/common/decorators'; export class ExplorerModel implements IDisposable { @@ -62,7 +63,6 @@ export class ExplorerModel implements IDisposable { } export class ExplorerItem { - private children?: Map; public parent: ExplorerItem; public isDirectoryResolved: boolean; @@ -107,21 +107,14 @@ export class ExplorerItem { return !!this._isError; } - set isDirectory(value: boolean) { - if (value !== this._isDirectory) { - this._isDirectory = value; - if (this._isDirectory) { - this.children = new Map(); - } else { - this.children = undefined; - } - } - } - get name(): string { return this._name; } + @memoize get children(): Map { + return new Map(); + } + private updateName(value: string): void { // Re-add to parent since the parent has a name map to children and the name might have changed if (this.parent) { @@ -186,7 +179,7 @@ export class ExplorerItem { // Properties local.resource = disk.resource; local.updateName(disk.name); - local.isDirectory = disk.isDirectory; + local._isDirectory = disk.isDirectory; local._mtime = disk.mtime; local.isDirectoryResolved = disk.isDirectoryResolved; local._isSymbolicLink = disk.isSymbolicLink; @@ -198,33 +191,29 @@ export class ExplorerItem { // Map resource => stat const oldLocalChildren = new ResourceMap(); - if (local.children) { - local.children.forEach(child => { - oldLocalChildren.set(child.resource, child); - }); - } + local.children.forEach(child => { + oldLocalChildren.set(child.resource, child); + }); // Clear current children - local.children = new Map(); + local.children.clear(); // Merge received children - if (disk.children) { - disk.children.forEach(diskChild => { - const formerLocalChild = oldLocalChildren.get(diskChild.resource); - // Existing child: merge - if (formerLocalChild) { - ExplorerItem.mergeLocalWithDisk(diskChild, formerLocalChild); - formerLocalChild.parent = local; - local.addChild(formerLocalChild); - } + disk.children.forEach(diskChild => { + const formerLocalChild = oldLocalChildren.get(diskChild.resource); + // Existing child: merge + if (formerLocalChild) { + ExplorerItem.mergeLocalWithDisk(diskChild, formerLocalChild); + formerLocalChild.parent = local; + local.addChild(formerLocalChild); + } - // New child: add - else { - diskChild.parent = local; - local.addChild(diskChild); - } - }); - } + // New child: add + else { + diskChild.parent = local; + local.addChild(diskChild); + } + }); } } @@ -232,24 +221,13 @@ export class ExplorerItem { * Adds a child element to this folder. */ addChild(child: ExplorerItem): void { - if (!this.children) { - this.isDirectory = true; - } - // Inherit some parent properties to child child.parent = this; child.updateResource(false); - - if (this.children) { - this.children.set(this.getPlatformAwareName(child.name), child); - } + this.children.set(this.getPlatformAwareName(child.name), child); } getChild(name: string): ExplorerItem | undefined { - if (!this.children) { - return undefined; - } - return this.children.get(this.getPlatformAwareName(name)); } @@ -273,21 +251,11 @@ export class ExplorerItem { }); } - getChildrenCount(): number { - if (!this.children) { - return 0; - } - - return this.children.size; - } - /** * Removes a child element from this folder. */ removeChild(child: ExplorerItem): void { - if (this.children) { - this.children.delete(this.getPlatformAwareName(child.name)); - } + this.children.delete(this.getPlatformAwareName(child.name)); } private getPlatformAwareName(name: string): string { @@ -308,7 +276,7 @@ export class ExplorerItem { this.resource = resources.joinPath(this.parent.resource, this.name); if (recursive) { - if (this.isDirectory && this.children) { + if (this.isDirectory) { this.children.forEach(child => { child.updateResource(true); }); @@ -350,7 +318,7 @@ export class ExplorerItem { return this; } - if (this.children) { + if (this.isDirectory) { // Ignore separtor to more easily deduct the next name to search while (index < path.length && path[index] === paths.sep) { index++; @@ -397,7 +365,6 @@ export class NewStatPlaceholder extends ExplorerItem { this.parent.removeChild(this); this.isDirectoryResolved = false; - this.isDirectory = false; } getId(): string { 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 b4133dfedd2..5e06f2fbe26 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 @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isUndefinedOrNull } from 'vs/base/common/types'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/paths'; @@ -35,10 +34,8 @@ suite('Files - View Model', () => { assert.strictEqual(s.name, 'sName'); assert.strictEqual(s.isDirectory, true); assert.strictEqual(s.mtime, new Date(d).getTime()); - assert.strictEqual(s.getChildrenArray().length, 0); s = createStat('/path/to/stat', 'sName', false, false, 8096, d); - assert(isUndefinedOrNull(s.getChildrenArray())); }); test('Add and Remove Child, check for hasChild', function () { @@ -50,14 +47,14 @@ suite('Files - View Model', () => { s.addChild(child1); - assert(s.getChildrenArray().length === 1); + assert(!!s.getChild(child1.name)); s.removeChild(child1); s.addChild(child1); - assert(s.getChildrenArray().length === 1); + assert(!!s.getChild(child1.name)); s.removeChild(child1); - assert(s.getChildrenArray().length === 0); + assert(!s.getChild(child1.name)); // Assert that adding a child updates its path properly s.addChild(child4); @@ -78,10 +75,6 @@ suite('Files - View Model', () => { s4.move(s1); - assert.strictEqual(s3.getChildrenArray().length, 0); - - assert.strictEqual(s1.getChildrenArray().length, 2); - // Assert the new path of the moved element assert.strictEqual(s4.resource.fsPath, toResource('/' + s4.name).fsPath); @@ -159,7 +152,7 @@ suite('Files - View Model', () => { assert.strictEqual(s1.find(s4Upper.resource), s4); } - assert.strictEqual(s1.find(toResource('foobar')), null); + assert.strictEqual(s1.find(toResource('foobar')), undefined); assert.strictEqual(s1.find(toResource('/')), s1); assert.strictEqual(s1.find(toResource('')), s1); @@ -277,7 +270,6 @@ suite('Files - View Model', () => { // Merge Child when isDirectoryResolved=false is a no-op merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d)); ExplorerItem.mergeLocalWithDisk(merge2, merge1); - assert.strictEqual(merge1.getChildrenArray().length, 0); // Merge Child with isDirectoryResolved=true const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d); @@ -285,7 +277,6 @@ suite('Files - View Model', () => { merge2.addChild(child); merge2.isDirectoryResolved = true; ExplorerItem.mergeLocalWithDisk(merge2, merge1); - assert.strictEqual(merge1.getChildrenArray().length, 1); assert.strictEqual(merge1.getChild('foo.html').name, 'foo.html'); assert.deepEqual(merge1.getChild('foo.html').parent, merge1, 'Check parent'); From 5915e1cb49fc054ad6ca5c0f845f56cbfc191769 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 13:57:02 +0100 Subject: [PATCH 0035/1066] explorer service: reorder methods --- .../files/electron-browser/explorerService.ts | 18 +++++---- .../electron-browser/views/explorerView.ts | 38 +++++++++---------- .../electron-browser/explorerModel.test.ts | 1 - 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 69fa9cf4aee..010305f551f 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -84,6 +84,8 @@ export class ExplorerService implements IExplorerService { return model; } + // IExplorerService methods + findClosest(resource: URI): ExplorerItem { return this.model.findClosest(resource); } @@ -121,14 +123,6 @@ export class ExplorerService implements IExplorerService { }, e => { this.notificationService.error(e); }); } - private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { - const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; - if (this.sortOrder !== configSortOrder) { - this.sortOrder = configSortOrder; - this.roots.forEach(r => this._onDidChangeItem.fire(r)); - } - } - // File events private onFileOperation(e: FileOperationEvent): void { @@ -297,6 +291,14 @@ export class ExplorerService implements IExplorerService { })); } + private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { + const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; + if (this.sortOrder !== configSortOrder) { + this.sortOrder = configSortOrder; + this.roots.forEach(r => this._onDidChangeItem.fire(r)); + } + } + dispose(): void { dispose(this.disposables); } 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 1e29f06b1aa..9fb1f7a9dfa 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -198,18 +198,6 @@ export class ExplorerView extends ViewletPanel { } } - private getActiveFile(): URI { - const input = this.editorService.activeEditor; - - // ignore diff editor inputs (helps to get out of diffing when returning to explorer) - if (input instanceof DiffEditorInput) { - return undefined; - } - - // check for files - return toResource(input, { supportSideBySide: true }); - } - private createTree(container: HTMLElement): void { this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); @@ -276,13 +264,6 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); } - getOptimalWidth(): number { - const parentNode = this.tree.getHTMLElement(); - const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels - - return DOM.getLargestChildWidth(parentNode, childNodes); - } - // React on events private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { @@ -357,6 +338,13 @@ export class ExplorerView extends ViewletPanel { return this.tree.refresh(toRefresh, true); } + getOptimalWidth(): number { + const parentNode = this.tree.getHTMLElement(); + const childNodes = ([] as HTMLElement[]).slice.call(parentNode.querySelectorAll('.explorer-item .label-name')); // select all file labels + + return DOM.getLargestChildWidth(parentNode, childNodes); + } + private setTreeInput(newRoots?: IWorkspaceFolder[]): Promise { if (!this.isVisible()) { this.shouldRefresh = true; @@ -392,6 +380,18 @@ export class ExplorerView extends ViewletPanel { return promise; } + private getActiveFile(): URI { + const input = this.editorService.activeEditor; + + // ignore diff editor inputs (helps to get out of diffing when returning to explorer) + if (input instanceof DiffEditorInput) { + return undefined; + } + + // check for files + return toResource(input, { supportSideBySide: true }); + } + private onSelectItem(fileStat: ExplorerItem, reveal = this.autoReveal): Promise { if (!fileStat || !this.isVisible()) { return Promise.resolve(void 0); 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 5e06f2fbe26..a6dd98de1bf 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 @@ -20,7 +20,6 @@ function toResource(path) { } else { return URI.file(join('/home/john', path)); } - } suite('Files - View Model', () => { From 676faed9e744726158296aaba6eafb2758941ad5 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 14:47:32 +0100 Subject: [PATCH 0036/1066] explorer: return null sometimes over undefined --- .../parts/files/common/explorerModel.ts | 24 +++++++++---------- .../files/electron-browser/explorerService.ts | 6 ++--- .../electron-browser/explorerModel.test.ts | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 83ce59db95d..a27e7df43d7 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -45,7 +45,7 @@ export class ExplorerModel implements IDisposable { * In case multiple FileStat are matching the resource (same folder opened multiple times) returns the FileStat that has the closest root. * Will return undefined in case the FileStat does not exist. */ - findClosest(resource: URI): ExplorerItem | undefined { + findClosest(resource: URI): ExplorerItem | null { const folder = this.contextService.getWorkspaceFolder(resource); if (folder) { const root = this.roots.filter(r => r.resource.toString() === folder.uri.toString()).pop(); @@ -54,7 +54,7 @@ export class ExplorerModel implements IDisposable { } } - return undefined; + return null; } dispose(): void { @@ -68,7 +68,7 @@ export class ExplorerItem { constructor( public resource: URI, - public root: ExplorerItem | undefined, + public root?: ExplorerItem, private _isSymbolicLink?: boolean, private _isReadonly?: boolean, private _isDirectory?: boolean, @@ -232,7 +232,7 @@ export class ExplorerItem { } fetchChildren(fileService: IFileService): Promise { - let promise = Promise.resolve(undefined); + let promise = Promise.resolve(null); if (!this.isDirectoryResolved) { promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { const resolved = ExplorerItem.create(stat, this.root); @@ -300,9 +300,9 @@ export class ExplorerItem { /** * Returns a child stat from this stat that matches with the provided path. - * Will return "undefined" in case the child does not exist. + * Will return "null" in case the child does not exist. */ - find(resource: URI): ExplorerItem | undefined { + find(resource: URI): ExplorerItem | null { // Return if path found // For performance reasons try to do the comparison as fast as possible if (resource && this.resource.scheme === resource.scheme && equalsIgnoreCase(this.resource.authority, resource.authority) && @@ -310,10 +310,10 @@ export class ExplorerItem { return this.findByPath(rtrim(resource.path, paths.sep), this.resource.path.length); } - return undefined; //Unable to find + return null; //Unable to find } - private findByPath(path: string, index: number): ExplorerItem | undefined { + private findByPath(path: string, index: number): ExplorerItem | null { if (paths.isEqual(rtrim(this.resource.path, paths.sep), path, !isLinux)) { return this; } @@ -340,7 +340,7 @@ export class ExplorerItem { } } - return undefined; + return null; } } @@ -353,7 +353,7 @@ export class NewStatPlaceholder extends ExplorerItem { private id: number; private directoryPlaceholder: boolean; - constructor(isDirectory: boolean, root: ExplorerItem | undefined) { + constructor(isDirectory: boolean, root: ExplorerItem) { super(URI.file(''), root, false, false, false, NewStatPlaceholder.NAME); this.id = NewStatPlaceholder.ID++; @@ -391,8 +391,8 @@ export class NewStatPlaceholder extends ExplorerItem { throw new Error('Can\'t perform operations in NewStatPlaceholder.'); } - find(resource: URI): ExplorerItem | undefined { - return undefined; + find(resource: URI): ExplorerItem | null { + return null; } static addNewStatPlaceholder(parent: ExplorerItem, isDirectory: boolean): NewStatPlaceholder { diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 010305f551f..dfe61969f5f 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -30,7 +30,7 @@ export class ExplorerService implements IExplorerService { private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first - private _onDidChangeItem = new Emitter(); + private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); private disposables: IDisposable[] = []; @@ -49,7 +49,7 @@ export class ExplorerService implements IExplorerService { return this.model.roots; } - get onDidChangeItem(): Event { + get onDidChangeItem(): Event { return this._onDidChangeItem.event; } @@ -79,7 +79,7 @@ export class ExplorerService implements IExplorerService { this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); - this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire())); + this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(null))); return model; } 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 a6dd98de1bf..b960ab81444 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 @@ -11,7 +11,7 @@ import { validateFileName } from 'vs/workbench/parts/files/electron-browser/file import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem { - return new ExplorerItem(toResource(path), undefined, false, false, isFolder, name, mtime); + return new ExplorerItem(toResource(path), null, false, false, isFolder, name, mtime); } function toResource(path) { @@ -151,7 +151,7 @@ suite('Files - View Model', () => { assert.strictEqual(s1.find(s4Upper.resource), s4); } - assert.strictEqual(s1.find(toResource('foobar')), undefined); + assert.strictEqual(s1.find(toResource('foobar')), null); assert.strictEqual(s1.find(toResource('/')), s1); assert.strictEqual(s1.find(toResource('')), s1); From ced64d2245d195cfd2b375753c481c1b5c372c2d Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 15:17:38 +0100 Subject: [PATCH 0037/1066] explorer: fix focus issues --- .../files/electron-browser/explorerService.ts | 2 +- .../files/electron-browser/fileActions.ts | 8 +------ .../electron-browser/views/explorerView.ts | 21 +++++++++++++++++-- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index dfe61969f5f..032afd931e9 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -103,7 +103,7 @@ export class ExplorerService implements IExplorerService { const fileStat = this.findClosest(resource); if (fileStat) { this._onDidSelectItem.fire({ item: fileStat, reveal }); - return Promise.resolve(void 0); + return Promise.resolve(null); } // Stat needs to be resolved first and then revealed diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 7764b2cbbf2..fb9cee81b7e 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1136,13 +1136,7 @@ export class FocusFilesExplorer extends Action { } public run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { - const view = viewlet.getExplorerView(); - if (view) { - view.setExpanded(true); - view.focus(); - } - }); + return this.viewletService.openViewlet(VIEWLET_ID, true); } } 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 9fb1f7a9dfa..44a2427898d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -170,7 +170,16 @@ export class ExplorerView extends ViewletPanel { this.onConfigurationUpdated(configuration); // When the explorer viewer is loaded, listen to changes to the editor input - this.disposables.push(this.editorService.onDidActiveEditorChange(() => this.explorerService.select(this.getActiveFile()))); + this.disposables.push(this.editorService.onDidActiveEditorChange(() => { + if (this.autoReveal) { + const activeFile = this.getActiveFile(); + if (activeFile) { + this.explorerService.select(this.getActiveFile()); + } else { + this.tree.setSelection([]); + } + } + })); // Also handle configuration updates this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); @@ -187,6 +196,10 @@ export class ExplorerView extends ViewletPanel { return actions; } + focus(): void { + this.tree.domFocus(); + } + setVisible(visible: boolean): void { super.setVisible(visible); if (visible) { @@ -235,6 +248,10 @@ export class ExplorerView extends ViewletPanel { // Open when selecting via keyboard this.disposables.push(this.tree.onDidChangeSelection(e => { + if (!e.browserEvent) { + // Only react on selection change events caused by user interaction (ignore those which are caused by us doing tree.setSelection). + return; + } const selection = e.elements; // Do not react if the user is expanding selection if (selection && selection.length === 1) { @@ -256,7 +273,7 @@ export class ExplorerView extends ViewletPanel { "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } }*/ this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } } })); From d9726e9e2adfef8fd3a42bd739c50a67daa082c7 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 15:38:09 +0100 Subject: [PATCH 0038/1066] explorer: simplify refresh --- .../electron-browser/views/explorerView.ts | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 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 44a2427898d..0b28669c009 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { ThrottledDelayer, sequence, ignoreErrors } from 'vs/base/common/async'; +import { sequence, ignoreErrors } 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'; @@ -44,13 +44,10 @@ import { onUnexpectedError } from 'vs/base/common/errors'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; - private static readonly EXPLORER_FILE_CHANGES_REFRESH_DELAY = 100; // delay in ms to refresh the explorer from disk file changes private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; - private explorerRefreshDelayer: ThrottledDelayer; - private resourceContext: ResourceContextKey; private folderContext: IContextKey; private readonlyContext: IContextKey; @@ -84,8 +81,6 @@ export class ExplorerView extends ViewletPanel { ) { super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); - this.explorerRefreshDelayer = new ThrottledDelayer(ExplorerView.EXPLORER_FILE_CHANGES_REFRESH_DELAY); - this.resourceContext = instantiationService.createInstance(ResourceContextKey); this.disposables.push(this.resourceContext); this.folderContext = ExplorerFolderContext.bindTo(contextKeyService); @@ -158,10 +153,10 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.setTreeInput())); this.disposables.push(this.labelService.onDidRegisterFormatter(() => { this._onDidChangeTitleArea.fire(); - this.refreshFromEvent(); + this.refresh(); })); - this.disposables.push(this.explorerService.onDidChangeItem(e => this.refreshFromEvent(e))); + this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); this.disposables.push(this.explorerService.onDidChangeEditable(e => this.refresh(e.parent))); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); @@ -303,16 +298,6 @@ export class ExplorerView extends ViewletPanel { } } - private refreshFromEvent(explorerItem?: ExplorerItem): void { - if (this.isVisible()) { - this.explorerRefreshDelayer.trigger(() => { - return this.refresh(explorerItem); - }); - } else { - this.shouldRefresh = true; - } - } - private onContextMenu(e: ITreeContextMenuEvent): void { const stat = e.element; if (stat instanceof NewStatPlaceholder) { @@ -347,7 +332,8 @@ export class ExplorerView extends ViewletPanel { * Refresh the contents of the explorer to get up to date data from the disk about the file structure. */ refresh(item?: ExplorerItem): Promise { - if (!this.tree) { + if (!this.tree || !this.isVisible()) { + this.shouldRefresh = true; return Promise.resolve(void 0); } const toRefresh = item || this.tree.getInput(); From a8b16bd91bef8b104dabda9adfe1755345c577da Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 16:11:32 +0100 Subject: [PATCH 0039/1066] explorer: sorter --- src/vs/workbench/parts/files/common/files.ts | 1 + .../files/electron-browser/explorerService.ts | 14 +- .../files/electron-browser/fileActions.ts | 4 +- .../electron-browser/views/explorerView.ts | 5 +- .../electron-browser/views/explorerViewer.ts | 162 ++++++++---------- 5 files changed, 87 insertions(+), 99 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 45787cca779..fa8e44ea004 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -41,6 +41,7 @@ export interface IEditableData { export interface IExplorerService { _serviceBrand: any; readonly roots: ExplorerItem[]; + readonly sortOrder: SortOrder; readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; readonly onDidSelectItem: Event<{ item: ExplorerItem, reveal: boolean }>; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 032afd931e9..6e2b3582f97 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -35,7 +35,7 @@ export class ExplorerService implements IExplorerService { private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); private disposables: IDisposable[] = []; private editableStats = new Map(); - private sortOrder: SortOrder; + private _sortOrder: SortOrder; constructor( @IFileService private fileService: IFileService, @@ -61,6 +61,10 @@ export class ExplorerService implements IExplorerService { return this._onDidSelectItem.event; } + get sortOrder(): SortOrder { + return this._sortOrder; + } + // Memoized locals @memoize private get fileEventsFilter(): ResourceGlobMatcher { const fileEventsFilter = this.instantiationService.createInstance( @@ -256,7 +260,7 @@ export class ExplorerService implements IExplorerService { } // Handle updated files/folders if we sort by modified - if (this.sortOrder === SortOrderConfiguration.MODIFIED) { + if (this._sortOrder === SortOrderConfiguration.MODIFIED) { const updated = e.getUpdated(); // Check updated: Refresh if updated file/folder part of resolved root @@ -275,7 +279,7 @@ export class ExplorerService implements IExplorerService { private filterToViewRelevantEvents(e: FileChangesEvent): FileChangesEvent { return new FileChangesEvent(e.changes.filter(change => { - if (change.type === FileChangeType.UPDATED && this.sortOrder !== SortOrderConfiguration.MODIFIED) { + if (change.type === FileChangeType.UPDATED && this._sortOrder !== SortOrderConfiguration.MODIFIED) { return false; // we only are about updated if we sort by modified time } @@ -293,8 +297,8 @@ export class ExplorerService implements IExplorerService { private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; - if (this.sortOrder !== configSortOrder) { - this.sortOrder = configSortOrder; + if (this._sortOrder !== configSortOrder) { + this._sortOrder = configSortOrder; this.roots.forEach(r => this._onDidChangeItem.fire(r)); } } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index fb9cee81b7e..5bc9f2a8206 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -804,7 +804,7 @@ class PasteFileAction extends BaseFileAction { // Copy File return this.fileService.copyFile(fileToPaste, targetFile).then(stat => { if (!stat.isDirectory) { - return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); } return void 0; @@ -838,7 +838,7 @@ export class DuplicateFileAction extends BaseFileAction { // Copy File const result = this.fileService.copyFile(this.element.resource, findValidPasteFileTarget(this.target, { resource: this.element.resource, isDirectory: this.element.isDirectory })).then(stat => { if (!stat.isDirectory) { - return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); } return void 0; 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 0b28669c009..0a7937330e8 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -31,7 +31,7 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter, FileSorter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; @@ -222,7 +222,8 @@ export class ExplorerView extends ViewletPanel { keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: stat => stat.name }, - filter: this.filter + filter: this.filter, + sorter: this.instantiationService.createInstance(FileSorter) }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.disposables.push(this.tree); 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 4ee028de2e6..5e99764c13c 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -15,7 +15,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { FileLabel, IFileLabelOptions } from 'vs/workbench/browser/labels'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -33,6 +33,7 @@ import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; import { IAction } from 'vs/base/common/actions'; import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { compareFileExtensions, compareFileNames } from 'vs/base/common/comparers'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -352,117 +353,98 @@ export class FilesFilter implements ITreeFilter { } // // Explorer Sorter -// export class FileSorter implements ISorter { -// private toDispose: IDisposable[]; -// private sortOrder: SortOrder; +export class FileSorter implements ITreeSorter { -// constructor( -// @IConfigurationService private configurationService: IConfigurationService, -// @IWorkspaceContextService private contextService: IWorkspaceContextService -// ) { -// this.toDispose = []; + constructor( + @IExplorerService private explorerService: IExplorerService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { } -// this.updateSortOrder(); + public compare(statA: ExplorerItem, statB: ExplorerItem): number { + // Do not sort roots + if (statA.isRoot) { + if (statB.isRoot) { + return this.contextService.getWorkspaceFolder(statA.resource).index - this.contextService.getWorkspaceFolder(statB.resource).index; + } -// this.registerListeners(); -// } + return -1; + } -// private registerListeners(): void { -// this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateSortOrder())); -// } + if (statB.isRoot) { + return 1; + } -// private updateSortOrder(): void { -// this.sortOrder = this.configurationService.getValue('explorer.sortOrder') || 'default'; -// } + const sortOrder = this.explorerService.sortOrder; -// public compare(tree: ITree, statA: ExplorerItem, statB: ExplorerItem): number { + // Sort Directories + switch (sortOrder) { + case 'type': + if (statA.isDirectory && !statB.isDirectory) { + return -1; + } -// // Do not sort roots -// if (statA.isRoot) { -// if (statB.isRoot) { -// return this.contextService.getWorkspaceFolder(statA.resource).index - this.contextService.getWorkspaceFolder(statB.resource).index; -// } + if (statB.isDirectory && !statA.isDirectory) { + return 1; + } -// return -1; -// } + if (statA.isDirectory && statB.isDirectory) { + return compareFileNames(statA.name, statB.name); + } -// if (statB.isRoot) { -// return 1; -// } + break; -// // Sort Directories -// switch (this.sortOrder) { -// case 'type': -// if (statA.isDirectory && !statB.isDirectory) { -// return -1; -// } + case 'filesFirst': + if (statA.isDirectory && !statB.isDirectory) { + return 1; + } -// if (statB.isDirectory && !statA.isDirectory) { -// return 1; -// } + if (statB.isDirectory && !statA.isDirectory) { + return -1; + } -// if (statA.isDirectory && statB.isDirectory) { -// return comparers.compareFileNames(statA.name, statB.name); -// } + break; -// break; + case 'mixed': + break; // not sorting when "mixed" is on -// case 'filesFirst': -// if (statA.isDirectory && !statB.isDirectory) { -// return 1; -// } + default: /* 'default', 'modified' */ + if (statA.isDirectory && !statB.isDirectory) { + return -1; + } -// if (statB.isDirectory && !statA.isDirectory) { -// return -1; -// } + if (statB.isDirectory && !statA.isDirectory) { + return 1; + } -// break; + break; + } -// case 'mixed': -// break; // not sorting when "mixed" is on + // Sort "New File/Folder" placeholders + if (statA instanceof NewStatPlaceholder) { + return -1; + } -// default: /* 'default', 'modified' */ -// if (statA.isDirectory && !statB.isDirectory) { -// return -1; -// } + if (statB instanceof NewStatPlaceholder) { + return 1; + } -// if (statB.isDirectory && !statA.isDirectory) { -// return 1; -// } + // Sort Files + switch (sortOrder) { + case 'type': + return compareFileExtensions(statA.name, statB.name); -// break; -// } + case 'modified': + if (statA.mtime !== statB.mtime) { + return statA.mtime < statB.mtime ? 1 : -1; + } -// // Sort "New File/Folder" placeholders -// if (statA instanceof NewStatPlaceholder) { -// return -1; -// } + return compareFileNames(statA.name, statB.name); -// if (statB instanceof NewStatPlaceholder) { -// return 1; -// } - -// // Sort Files -// switch (this.sortOrder) { -// case 'type': -// return comparers.compareFileExtensions(statA.name, statB.name); - -// case 'modified': -// if (statA.mtime !== statB.mtime) { -// return statA.mtime < statB.mtime ? 1 : -1; -// } - -// return comparers.compareFileNames(statA.name, statB.name); - -// default: /* 'default', 'mixed', 'filesFirst' */ -// return comparers.compareFileNames(statA.name, statB.name); -// } -// } - -// public dispose(): void { -// this.toDispose = dispose(this.toDispose); -// } -// } + default: /* 'default', 'mixed', 'filesFirst' */ + return compareFileNames(statA.name, statB.name); + } + } +} // export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { From c075795f7e2a1d6f60891299859507bcd1204937 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 17:01:53 +0100 Subject: [PATCH 0040/1066] file actions: some cleanup --- .../files/electron-browser/fileActions.ts | 172 ++++-------------- 1 file changed, 34 insertions(+), 138 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 5bc9f2a8206..cd753f21ff5 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -35,7 +35,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IListService, ListWidget } from 'vs/platform/list/browser/listService'; -import { RawContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Schemas } from 'vs/base/common/network'; import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; @@ -100,49 +100,22 @@ export class BaseErrorReportingAction extends Action { } } -export class BaseFileAction extends BaseErrorReportingAction { - public element: ExplorerItem; - - constructor( - id: string, - label: string, - @IFileService protected fileService: IFileService, - @INotificationService notificationService: INotificationService, - @ITextFileService protected textFileService: ITextFileService - ) { - super(id, label, notificationService); - - this.enabled = false; - } - - _isEnabled(): boolean { - return true; - } - - _updateEnablement(): void { - this.enabled = !!(this.fileService && this._isEnabled()); - } -} - -class TriggerRenameFileAction extends BaseFileAction { +class TriggerRenameFileAction extends BaseErrorReportingAction { public static readonly ID = 'renameFile'; private renameAction: BaseRenameAction; constructor( - element: ExplorerItem, - @IFileService fileService: IFileService, + private element: ExplorerItem, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService, @IExplorerService private explorerService: IExplorerService ) { - super(TriggerRenameFileAction.ID, TRIGGER_RENAME_LABEL, fileService, notificationService, textFileService); + super(TriggerRenameFileAction.ID, TRIGGER_RENAME_LABEL, notificationService); this.element = element; this.renameAction = instantiationService.createInstance(RenameFileAction, element); - this._updateEnablement(); } public validateFileName(name: string): string { @@ -163,23 +136,16 @@ class TriggerRenameFileAction extends BaseFileAction { } } -export abstract class BaseRenameAction extends BaseFileAction { +export abstract class BaseRenameAction extends BaseErrorReportingAction { constructor( id: string, label: string, - element: ExplorerItem, - @IFileService fileService: IFileService, + public element: ExplorerItem, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService ) { - super(id, label, fileService, notificationService, textFileService); - - this.element = element; - } - - _isEnabled(): boolean { - return super._isEnabled() && this.element && !this.element.isReadonly; + super(id, label, notificationService); + this.enabled = this.element && !this.element.isReadonly; } public run(context?: any): Promise { @@ -234,13 +200,10 @@ class RenameFileAction extends BaseRenameAction { constructor( element: ExplorerItem, - @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService + @ITextFileService private textFileService: ITextFileService ) { - super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, notificationService, textFileService); - - this._updateEnablement(); + super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, notificationService); } public runAction(newName: string): Promise { @@ -252,7 +215,7 @@ class RenameFileAction extends BaseRenameAction { } /* Base New File/Folder Action */ -export class BaseNewAction extends BaseFileAction { +export class BaseNewAction extends BaseErrorReportingAction { private presetFolder: ExplorerItem; constructor( @@ -261,12 +224,10 @@ export class BaseNewAction extends BaseFileAction { private isFile: boolean, private renameAction: BaseRenameAction, element: ExplorerItem, - @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, @IExplorerService private explorerService: IExplorerService ) { - super(id, label, fileService, notificationService, textFileService); + super(id, label, notificationService); if (element) { this.presetFolder = element.isDirectory ? element : element.parent; @@ -299,19 +260,14 @@ export class BaseNewAction extends BaseFileAction { /* New File */ export class NewFileAction extends BaseNewAction { - constructor( element: ExplorerItem, - @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService, @IExplorerService explorerService: IExplorerService ) { - super('explorer.newFile', NEW_FILE_LABEL, true, instantiationService.createInstance(CreateFileAction, element), null, fileService, notificationService, textFileService, explorerService); - + super('explorer.newFile', NEW_FILE_LABEL, true, instantiationService.createInstance(CreateFileAction, element), null, notificationService, explorerService); this.class = 'explorer-action new-file'; - this._updateEnablement(); } } @@ -320,16 +276,12 @@ export class NewFolderAction extends BaseNewAction { constructor( element: ExplorerItem, - @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, @IInstantiationService instantiationService: IInstantiationService, @IExplorerService explorerService: IExplorerService ) { - super('explorer.newFolder', NEW_FOLDER_LABEL, false, instantiationService.createInstance(CreateFolderAction, element), null, fileService, notificationService, textFileService, explorerService); - + super('explorer.newFolder', NEW_FOLDER_LABEL, false, instantiationService.createInstance(CreateFolderAction, element), null, notificationService, explorerService); this.class = 'explorer-action new-folder'; - this._updateEnablement(); } } @@ -371,14 +323,11 @@ class CreateFileAction extends BaseCreateAction { constructor( element: ExplorerItem, - @IFileService fileService: IFileService, @IEditorService private editorService: IEditorService, + @IFileService private fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService ) { - super(CreateFileAction.ID, CreateFileAction.LABEL, element, fileService, notificationService, textFileService); - - this._updateEnablement(); + super(CreateFileAction.ID, CreateFileAction.LABEL, element, notificationService); } public runAction(fileName: string): Promise { @@ -399,13 +348,10 @@ class CreateFolderAction extends BaseCreateAction { constructor( element: ExplorerItem, - @IFileService fileService: IFileService, + @IFileService private fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService ) { - super(CreateFolderAction.ID, CreateFolderAction.LABEL, null, fileService, notificationService, textFileService); - - this._updateEnablement(); + super(CreateFolderAction.ID, CreateFolderAction.LABEL, element, notificationService); } public runAction(fileName: string): Promise { @@ -416,7 +362,7 @@ class CreateFolderAction extends BaseCreateAction { } } -class BaseDeleteFileAction extends BaseFileAction { +class BaseDeleteFileAction extends BaseErrorReportingAction { private static readonly CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; @@ -425,21 +371,16 @@ class BaseDeleteFileAction extends BaseFileAction { constructor( private elements: ExplorerItem[], private useTrash: boolean, - @IFileService fileService: IFileService, + @IFileService private fileService: IFileService, @INotificationService notificationService: INotificationService, @IDialogService private dialogService: IDialogService, - @ITextFileService textFileService: ITextFileService, + @ITextFileService private textFileService: ITextFileService, @IConfigurationService private configurationService: IConfigurationService ) { - super('moveFileToTrash', MOVE_FILE_TO_TRASH_LABEL, fileService, notificationService, textFileService); + super('moveFileToTrash', MOVE_FILE_TO_TRASH_LABEL, notificationService); this.useTrash = useTrash && elements.every(e => !paths.isUNC(e.resource.fsPath)); // on UNC shares there is no trash - - this._updateEnablement(); - } - - _isEnabled(): boolean { - return super._isEnabled() && this.elements && this.elements.every(e => !e.isReadonly); + this.enabled = this.elements && this.elements.every(e => !e.isReadonly); } public run(): Promise { @@ -631,28 +572,24 @@ class BaseDeleteFileAction extends BaseFileAction { } /* Add File */ -export class AddFilesAction extends BaseFileAction { +export class AddFilesAction extends BaseErrorReportingAction { constructor( - element: ExplorerItem, + private element: ExplorerItem, clazz: string, - @IFileService fileService: IFileService, + @IFileService private fileService: IFileService, @IEditorService private editorService: IEditorService, @IDialogService private dialogService: IDialogService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, + @ITextFileService private textFileService: ITextFileService, @IExplorerService private explorerService: IExplorerService ) { - super('workbench.files.action.addFile', nls.localize('addFiles', "Add Files"), fileService, notificationService, textFileService); - - this.element = element; + super('workbench.files.action.addFile', nls.localize('addFiles', "Add Files"), notificationService); if (clazz) { this.class = clazz; } - - this._updateEnablement(); } public run(resourcesToAdd: URI[]): Promise { @@ -736,19 +673,14 @@ export class AddFilesAction extends BaseFileAction { } // Copy File/Folder -class CopyFileAction extends BaseFileAction { +class CopyFileAction extends BaseErrorReportingAction { constructor( private elements: ExplorerItem[], - @IFileService fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, - @IContextKeyService contextKeyService: IContextKeyService, @IClipboardService private clipboardService: IClipboardService ) { - super('filesExplorer.copy', COPY_FILE_LABEL, fileService, notificationService, textFileService); - - this._updateEnablement(); + super('filesExplorer.copy', COPY_FILE_LABEL, notificationService); } public run(): Promise { @@ -761,25 +693,22 @@ class CopyFileAction extends BaseFileAction { } // Paste File/Folder -class PasteFileAction extends BaseFileAction { +class PasteFileAction extends BaseErrorReportingAction { public static readonly ID = 'filesExplorer.paste'; constructor( - element: ExplorerItem, - @IFileService fileService: IFileService, + private element: ExplorerItem, + @IFileService private fileService: IFileService, @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService, @IEditorService private editorService: IEditorService, @IExplorerService private explorerService: IExplorerService ) { - super(PasteFileAction.ID, PASTE_FILE_LABEL, fileService, notificationService, textFileService); + super(PasteFileAction.ID, PASTE_FILE_LABEL, notificationService); - this.element = element; if (!this.element) { this.element = this.explorerService.roots[0]; } - this._updateEnablement(); } public run(fileToPaste: URI): Promise { @@ -815,39 +744,6 @@ class PasteFileAction extends BaseFileAction { } } -// Duplicate File/Folder -export class DuplicateFileAction extends BaseFileAction { - private target: ExplorerItem; - - constructor( - fileToDuplicate: ExplorerItem, - target: ExplorerItem, - @IFileService fileService: IFileService, - @IEditorService private editorService: IEditorService, - @INotificationService notificationService: INotificationService, - @ITextFileService textFileService: ITextFileService - ) { - super('workbench.files.action.duplicateFile', nls.localize('duplicateFile', "Duplicate"), fileService, notificationService, textFileService); - - this.element = fileToDuplicate; - this.target = (target && target.isDirectory) ? target : fileToDuplicate.parent; - this._updateEnablement(); - } - - public run(): Promise { - // Copy File - const result = this.fileService.copyFile(this.element.resource, findValidPasteFileTarget(this.target, { resource: this.element.resource, isDirectory: this.element.isDirectory })).then(stat => { - if (!stat.isDirectory) { - return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); - } - - return void 0; - }, error => this.onError(error)); - - return result; - } -} - function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean }): URI { let name = resources.basenameOrAuthority(fileToPaste.resource); From fac0722dc8f931d2680ad3bcb80521a075a3210c Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 19 Dec 2018 18:00:45 +0100 Subject: [PATCH 0041/1066] explorer: input box shananigens --- .../parts/files/common/explorerModel.ts | 97 ++++--------------- .../files/electron-browser/explorerService.ts | 6 +- .../files/electron-browser/fileActions.ts | 29 ++---- .../electron-browser/views/explorerView.ts | 5 +- .../electron-browser/views/explorerViewer.ts | 21 ++-- .../electron-browser/explorerModel.test.ts | 10 +- 6 files changed, 48 insertions(+), 120 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index a27e7df43d7..5a351c78900 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -22,7 +22,7 @@ export class ExplorerModel implements IDisposable { constructor(private contextService: IWorkspaceContextService) { const setRoots = () => this._roots = this.contextService.getWorkspace().folders - .map(folder => new ExplorerItem(folder.uri, undefined, false, false, true, folder.name)); + .map(folder => new ExplorerItem(folder.uri, undefined, true, false, false, folder.name)); this._listener = this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); setRoots(); } @@ -63,23 +63,19 @@ export class ExplorerModel implements IDisposable { } export class ExplorerItem { - public parent: ExplorerItem; public isDirectoryResolved: boolean; constructor( public resource: URI, - public root?: ExplorerItem, + private _parent: ExplorerItem, + private _isDirectory?: boolean, private _isSymbolicLink?: boolean, private _isReadonly?: boolean, - private _isDirectory?: boolean, private _name: string = resources.basenameOrAuthority(resource), private _mtime?: number, private _etag?: string, private _isError?: boolean ) { - if (!this.root) { - this.root = this; - } this.isDirectoryResolved = false; } @@ -111,6 +107,18 @@ export class ExplorerItem { return this._name; } + get parent(): ExplorerItem { + return this._parent; + } + + get root(): ExplorerItem { + if (!this._parent) { + return this; + } + + return this.parent.root; + } + @memoize get children(): Map { return new Map(); } @@ -134,8 +142,8 @@ export class ExplorerItem { return this === this.root; } - static create(raw: IFileStat, root: ExplorerItem, resolveTo?: URI[], isError = false): ExplorerItem { - const stat = new ExplorerItem(raw.resource, root, raw.isSymbolicLink, raw.isReadonly, raw.isDirectory, raw.name, raw.mtime, raw.etag, isError); + static create(raw: IFileStat, parent: ExplorerItem, resolveTo?: URI[], isError = false): ExplorerItem { + const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag, isError); // Recursively add children if present if (stat.isDirectory) { @@ -150,8 +158,7 @@ export class ExplorerItem { // Recurse into children if (raw.children) { for (let i = 0, len = raw.children.length; i < len; i++) { - const child = ExplorerItem.create(raw.children[i], root, resolveTo); - child.parent = stat; + const child = ExplorerItem.create(raw.children[i], stat, resolveTo); stat.addChild(child); } } @@ -204,13 +211,11 @@ export class ExplorerItem { // Existing child: merge if (formerLocalChild) { ExplorerItem.mergeLocalWithDisk(diskChild, formerLocalChild); - formerLocalChild.parent = local; local.addChild(formerLocalChild); } // New child: add else { - diskChild.parent = local; local.addChild(diskChild); } }); @@ -222,7 +227,7 @@ export class ExplorerItem { */ addChild(child: ExplorerItem): void { // Inherit some parent properties to child - child.parent = this; + child._parent = this; child.updateResource(false); this.children.set(this.getPlatformAwareName(child.name), child); } @@ -235,7 +240,7 @@ export class ExplorerItem { let promise = Promise.resolve(null); if (!this.isDirectoryResolved) { promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { - const resolved = ExplorerItem.create(stat, this.root); + const resolved = ExplorerItem.create(stat, this); ExplorerItem.mergeLocalWithDisk(resolved, this); this.isDirectoryResolved = true; }); @@ -343,65 +348,3 @@ export class ExplorerItem { return null; } } - -/* A helper that can be used to show a placeholder when creating a new stat */ -export class NewStatPlaceholder extends ExplorerItem { - - static readonly NAME = ''; - private static ID = 0; - - private id: number; - private directoryPlaceholder: boolean; - - constructor(isDirectory: boolean, root: ExplorerItem) { - super(URI.file(''), root, false, false, false, NewStatPlaceholder.NAME); - - this.id = NewStatPlaceholder.ID++; - this.isDirectoryResolved = isDirectory; - this.directoryPlaceholder = isDirectory; - } - - destroy(): void { - this.parent.removeChild(this); - - this.isDirectoryResolved = false; - } - - getId(): string { - return `new-stat-placeholder:${this.id}:${this.parent.resource.toString()}`; - } - - isDirectoryPlaceholder(): boolean { - return this.directoryPlaceholder; - } - - addChild() { - throw new Error('Can\'t perform operations in NewStatPlaceholder.'); - } - - removeChild() { - throw new Error('Can\'t perform operations in NewStatPlaceholder.'); - } - - move() { - throw new Error('Can\'t perform operations in NewStatPlaceholder.'); - } - - rename() { - throw new Error('Can\'t perform operations in NewStatPlaceholder.'); - } - - find(resource: URI): ExplorerItem | null { - return null; - } - - static addNewStatPlaceholder(parent: ExplorerItem, isDirectory: boolean): NewStatPlaceholder { - const child = new NewStatPlaceholder(isDirectory, parent.root); - - // Inherit some parent properties to child - child.parent = parent; - parent.addChild(child); - - return child; - } -} diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 6e2b3582f97..3df4b26d8d1 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -118,7 +118,7 @@ export class ExplorerService implements IExplorerService { // Convert to model const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); - const modelStat = ExplorerItem.create(stat, root, options.resolveTo); + const modelStat = ExplorerItem.create(stat, null, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); @@ -144,11 +144,11 @@ export class ExplorerService implements IExplorerService { const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource); thenable.then(stat => { if (stat) { - const modelStat = ExplorerItem.create(stat, p.root); + const modelStat = ExplorerItem.create(stat, p.parent); ExplorerItem.mergeLocalWithDisk(modelStat, p); } - const childElement = ExplorerItem.create(addedElement, p.root); + const childElement = ExplorerItem.create(addedElement, p.parent); // Make sure to remove any previous version of the file if any p.removeChild(childElement); p.addChild(childElement); diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index cd753f21ff5..aa5e42545b2 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -45,7 +45,7 @@ import { CLOSE_EDITORS_AND_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/e import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; export interface IEditableData { action: IAction; @@ -217,6 +217,7 @@ class RenameFileAction extends BaseRenameAction { /* Base New File/Folder Action */ export class BaseNewAction extends BaseErrorReportingAction { private presetFolder: ExplorerItem; + static readonly PLACEHOLDER_URI = URI.file(''); constructor( id: string, @@ -244,13 +245,9 @@ export class BaseNewAction extends BaseErrorReportingAction { if (folder.isReadonly) { return Promise.reject(new Error('Parent folder is readonly.')); } - if (!!folder.getChild(NewStatPlaceholder.NAME)) { - // Do not allow to creatae a new file/folder while in the process of creating a new file/folder #47606 - return Promise.resolve(new Error('Parent folder is already in the process of creating a file')); - } - - const stat = NewStatPlaceholder.addNewStatPlaceholder(folder, !this.isFile); + const stat = new ExplorerItem(BaseNewAction.PLACEHOLDER_URI, folder, !this.isFile); + folder.addChild(stat); this.renameAction.element = stat; this.explorerService.setEditable(stat, { action: this.renameAction, validationMessage: value => this.renameAction.validateFileName(folder, value) }); @@ -303,20 +300,8 @@ export class GlobalNewUntitledFileAction extends Action { } } -/* Create New File/Folder (only used internally by explorerViewer) */ -export abstract class BaseCreateAction extends BaseRenameAction { - - public validateFileName(parent: ExplorerItem, name: string): string { - if (this.element instanceof NewStatPlaceholder) { - return validateFileName(parent, name); - } - - return super.validateFileName(parent, name); - } -} - /* Create New File (only used internally by explorerViewer) */ -class CreateFileAction extends BaseCreateAction { +class CreateFileAction extends BaseRenameAction { public static readonly ID = 'workbench.files.action.createFileFromExplorer'; public static readonly LABEL = nls.localize('createNewFile', "New File"); @@ -332,6 +317,7 @@ class CreateFileAction extends BaseCreateAction { public runAction(fileName: string): Promise { const resource = this.element.parent.resource; + this.element.parent.removeChild(this.element); return this.fileService.createFile(resources.joinPath(resource, fileName)).then(stat => { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); }, (error) => { @@ -341,7 +327,7 @@ class CreateFileAction extends BaseCreateAction { } /* Create New Folder (only used internally by explorerViewer) */ -class CreateFolderAction extends BaseCreateAction { +class CreateFolderAction extends BaseRenameAction { public static readonly ID = 'workbench.files.action.createFolderFromExplorer'; public static readonly LABEL = nls.localize('createNewFolder', "New Folder"); @@ -356,6 +342,7 @@ class CreateFolderAction extends BaseCreateAction { public runAction(fileName: string): Promise { const resource = this.element.parent.resource; + this.element.parent.removeChild(this.element); return this.fileService.createFolder(resources.joinPath(resource, fileName)).then(void 0, (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); }); 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 0a7937330e8..f2d5cd75b5b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -39,7 +39,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; export class ExplorerView extends ViewletPanel { @@ -301,9 +301,6 @@ export class ExplorerView extends ViewletPanel { private onContextMenu(e: ITreeContextMenuEvent): void { const stat = e.element; - if (stat instanceof NewStatPlaceholder) { - return; - } // update dynamic contexts this.fileCopiedContextKey.set(this.clipboardService.hasResources()); 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 5e99764c13c..781dc787609 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -32,7 +32,7 @@ import { rtrim } from 'vs/base/common/strings'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; import { IAction } from 'vs/base/common/actions'; -import { ExplorerItem, NewStatPlaceholder } from 'vs/workbench/parts/files/common/explorerModel'; +import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { compareFileExtensions, compareFileNames } from 'vs/base/common/comparers'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -154,7 +154,7 @@ export class FilesRenderer implements ITreeRenderer { label.element.style.display = 'none'; - if (stat instanceof NewStatPlaceholder) { - stat.destroy(); - } + if (!commit) { + stat.parent.removeChild(stat); + } if (commit && inputBox.value) { await editableData.action.run({ value: inputBox.value }); } dispose(toDispose); container.removeChild(label.element); - // todo@isidor need to unset editable data + this.explorerService.setEditable(stat, null); }); const toDispose = [ @@ -305,7 +305,8 @@ export class FilesFilter implements ITreeFilter { constructor( @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IConfigurationService private configurationService: IConfigurationService + @IConfigurationService private configurationService: IConfigurationService, + @IExplorerService private explorerService: IExplorerService ) { this.hiddenExpressionPerRoot = new Map(); this.workspaceFolderChangeListener = this.contextService.onDidChangeWorkspaceFolders(() => this.updateConfiguration()); @@ -334,7 +335,7 @@ export class FilesFilter implements ITreeFilter { if (parentVisibility === TreeVisibility.Hidden) { return false; } - if (stat instanceof NewStatPlaceholder || stat.isRoot) { + if (this.explorerService.getEditableData(stat) || stat.isRoot) { return true; // always visible } @@ -420,11 +421,11 @@ export class FileSorter implements ITreeSorter { } // Sort "New File/Folder" placeholders - if (statA instanceof NewStatPlaceholder) { + if (this.explorerService.getEditableData(statA)) { return -1; } - if (statB instanceof NewStatPlaceholder) { + if (this.explorerService.getEditableData(statB)) { return 1; } 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 b960ab81444..6f366a97b49 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 @@ -11,7 +11,7 @@ import { validateFileName } from 'vs/workbench/parts/files/electron-browser/file import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; function createStat(path: string, name: string, isFolder: boolean, hasChildren: boolean, size: number, mtime: number): ExplorerItem { - return new ExplorerItem(toResource(path), null, false, false, isFolder, name, mtime); + return new ExplorerItem(toResource(path), null, isFolder, false, false, name, mtime); } function toResource(path) { @@ -259,19 +259,19 @@ suite('Files - View Model', () => { test('Merge Local with Disk', function () { const d = new Date().toUTCString(); - const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, false, true, 'to', Date.now(), d); - const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, false, false, true, 'to', Date.now(), new Date(0).toUTCString()); + const merge1 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), d); + const merge2 = new ExplorerItem(URI.file(join('C:\\', '/path/to')), undefined, true, false, false, 'to', Date.now(), new Date(0).toUTCString()); // Merge Properties ExplorerItem.mergeLocalWithDisk(merge2, merge1); assert.strictEqual(merge1.mtime, merge2.mtime); // Merge Child when isDirectoryResolved=false is a no-op - merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d)); + merge2.addChild(new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, true, false, false, 'foo.html', Date.now(), d)); ExplorerItem.mergeLocalWithDisk(merge2, merge1); // Merge Child with isDirectoryResolved=true - const child = new ExplorerItem(URI.file(join('C:\\', '/path/to/foo.html')), undefined, false, false, true, 'foo.html', Date.now(), d); + 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; From 5ca44a5a1221869c6e1bf93c31d11ee2f09cb4ee Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 16:03:25 +0100 Subject: [PATCH 0042/1066] explorer service lazy instantitation --- .../parts/files/electron-browser/files.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index 2399ebd3771..b325009c33e 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -81,7 +81,7 @@ Registry.as(ViewletExtensions.Viewlets).registerViewlet(new Vie 0 )); -registerSingleton(IExplorerService, ExplorerService); +registerSingleton(IExplorerService, ExplorerService, true); Registry.as(ViewletExtensions.Viewlets).setDefaultViewletId(VIEWLET_ID); From ca426e1fcec667d27bccc78fc01d3a0db40860b8 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 16:13:18 +0100 Subject: [PATCH 0043/1066] explorer adopt to onDidChangeBodyVisibility --- .../electron-browser/views/explorerView.ts | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 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 bc3dde7a3f0..19b0a8e1a2b 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -179,6 +179,16 @@ export class ExplorerView extends ViewletPanel { // Also handle configuration updates this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); + + this.disposables.push(this.onDidChangeBodyVisibility(visible => { + if (visible) { + // If a refresh was requested and we are now visible, run it + if (this.shouldRefresh) { + this.shouldRefresh = false; + this.setTreeInput().then(undefined, onUnexpectedError); + } + } + })); } getActions(): IAction[] { @@ -196,17 +206,6 @@ export class ExplorerView extends ViewletPanel { this.tree.domFocus(); } - setVisible(visible: boolean): void { - super.setVisible(visible); - if (visible) { - // If a refresh was requested and we are now visible, run it - if (this.shouldRefresh) { - this.shouldRefresh = false; - this.setTreeInput().then(undefined, onUnexpectedError); - } - } - } - private createTree(container: HTMLElement): void { this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); From 137248638623c286b83a4a7f4f05121da246b75a Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 16:30:09 +0100 Subject: [PATCH 0044/1066] manualy call setInput --- .../parts/files/electron-browser/views/explorerView.ts | 3 ++- 1 file changed, 2 insertions(+), 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 19b0a8e1a2b..8f2efd87c51 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -55,7 +55,7 @@ export class ExplorerView extends ViewletPanel { private rootContext: IContextKey; // Refresh is needed on the initial explorer open - private shouldRefresh = true; + private shouldRefresh; private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; @@ -229,6 +229,7 @@ export class ExplorerView extends ViewletPanel { sorter: this.instantiationService.createInstance(FileSorter) }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); + this.setTreeInput(); this.disposables.push(this.tree); // Bind context keys FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); From 3f9bbaebad6d43b1464f9a94332c70a3b205ddad Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 17:16:58 +0100 Subject: [PATCH 0045/1066] explorer: fix multi root issues --- .../parts/files/common/explorerModel.ts | 12 +++++++- src/vs/workbench/parts/files/common/files.ts | 1 + .../files/electron-browser/explorerService.ts | 11 +++++++- .../electron-browser/views/explorerView.ts | 28 ++++++++++--------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 5a351c78900..e5a7487021e 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -14,23 +14,33 @@ import { coalesce } from 'vs/base/common/arrays'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { memoize } from 'vs/base/common/decorators'; +import { Emitter, Event } from 'vs/base/common/event'; export class ExplorerModel implements IDisposable { private _roots: ExplorerItem[]; private _listener: IDisposable; + private _onDidChangeRoots = new Emitter(); constructor(private contextService: IWorkspaceContextService) { const setRoots = () => this._roots = this.contextService.getWorkspace().folders .map(folder => new ExplorerItem(folder.uri, undefined, true, false, false, folder.name)); - this._listener = this.contextService.onDidChangeWorkspaceFolders(() => setRoots()); setRoots(); + + this._listener = this.contextService.onDidChangeWorkspaceFolders(() => { + setRoots(); + this._onDidChangeRoots.fire(); + }); } get roots(): ExplorerItem[] { return this._roots; } + get onDidChangeRoots(): Event { + return this._onDidChangeRoots.event; + } + /** * Returns an array of child stat from this stat that matches with the provided path. * Starts matching from the first root. diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index fa8e44ea004..f215333699d 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -42,6 +42,7 @@ export interface IExplorerService { _serviceBrand: any; readonly roots: ExplorerItem[]; readonly sortOrder: SortOrder; + readonly onDidChangeRoots: Event; readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; readonly onDidSelectItem: Event<{ item: ExplorerItem, reveal: boolean }>; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 3df4b26d8d1..9af0b4eb53b 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -30,6 +30,7 @@ export class ExplorerService implements IExplorerService { private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first + private _onDidChangeRoots = new Emitter(); private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); @@ -49,6 +50,10 @@ export class ExplorerService implements IExplorerService { return this.model.roots; } + get onDidChangeRoots(): Event { + return this._onDidChangeRoots.event; + } + get onDidChangeItem(): Event { return this._onDidChangeItem.event; } @@ -84,6 +89,7 @@ export class ExplorerService implements IExplorerService { this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(null))); + this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire())); return model; } @@ -298,8 +304,11 @@ export class ExplorerService implements IExplorerService { private onConfigurationUpdated(configuration: IFilesConfiguration, event?: IConfigurationChangeEvent): void { const configSortOrder = configuration && configuration.explorer && configuration.explorer.sortOrder || 'default'; if (this._sortOrder !== configSortOrder) { + const shouldFire = this._sortOrder !== undefined; this._sortOrder = configSortOrder; - this.roots.forEach(r => this._onDidChangeItem.fire(r)); + if (shouldFire) { + this._onDidChangeRoots.fire(); + } } } 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 8f2efd87c51..febda637939 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -17,7 +17,7 @@ import * as DOM from 'vs/base/browser/dom'; import { CollapseAction2 } from 'vs/workbench/browser/viewlet'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { ExplorerDecorationsProvider } from 'vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider'; -import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -150,13 +150,13 @@ export class ExplorerView extends ViewletPanel { this.toolbar.setActions(this.getActions(), this.getSecondaryActions())(); } - this.disposables.push(this.contextService.onDidChangeWorkspaceFolders(e => this.setTreeInput(e.added))); this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.setTreeInput())); this.disposables.push(this.labelService.onDidRegisterFormatter(() => { this._onDidChangeTitleArea.fire(); this.refresh(); })); + this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput())); this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); this.disposables.push(this.explorerService.onDidChangeEditable(e => this.refresh(e.parent))); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); @@ -350,13 +350,16 @@ export class ExplorerView extends ViewletPanel { return DOM.getLargestChildWidth(parentNode, childNodes); } - private setTreeInput(newRoots?: IWorkspaceFolder[]): Promise { + private setTreeInput(): Promise { if (!this.isVisible()) { this.shouldRefresh = true; return Promise.resolve(void 0); } - perf.mark('willResolveExplorer'); + const initialInputSetup = !this.tree.getInput(); + if (initialInputSetup) { + perf.mark('willResolveExplorer'); + } const roots = this.explorerService.roots; let input: ExplorerItem | ExplorerItem[] = roots[0]; if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER || roots[0].isError) { @@ -365,21 +368,20 @@ export class ExplorerView extends ViewletPanel { } const promise = this.tree.setInput(input).then(() => { - let expandPromise = Promise.resolve(void 0); - if (newRoots && newRoots.length) { - expandPromise = Promise.all(newRoots.map(workspaceFolder => this.tree.expand(this.explorerService.findClosest(workspaceFolder.uri)))); - } - // Find resource to focus from active editor input if set - if (this.autoReveal) { + if (this.autoReveal && initialInputSetup) { const resourceToFocus = this.getActiveFile(); if (resourceToFocus) { - return expandPromise.then(() => this.explorerService.select(resourceToFocus, true)); + return this.explorerService.select(resourceToFocus, true); } } - return expandPromise; - }).then(() => perf.mark('didResolveExplorer')); + return undefined; + }).then(() => { + if (initialInputSetup) { + perf.mark('didResolveExplorer'); + } + }); this.progressService.showWhile(promise, this.partService.isRestored() ? 800 : 1200 /* less ugly initial startup */); return promise; From f4713f023bc49323aea4dc84b097927f60f62cd9 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 17:26:21 +0100 Subject: [PATCH 0046/1066] explorer: make sure newly created folder gets selected --- src/vs/workbench/parts/files/electron-browser/fileActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index aa5e42545b2..e04d0bb6edc 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -336,6 +336,7 @@ class CreateFolderAction extends BaseRenameAction { element: ExplorerItem, @IFileService private fileService: IFileService, @INotificationService notificationService: INotificationService, + @IExplorerService private explorerService: IExplorerService ) { super(CreateFolderAction.ID, CreateFolderAction.LABEL, element, notificationService); } @@ -343,7 +344,8 @@ class CreateFolderAction extends BaseRenameAction { public runAction(fileName: string): Promise { const resource = this.element.parent.resource; this.element.parent.removeChild(this.element); - return this.fileService.createFolder(resources.joinPath(resource, fileName)).then(void 0, (error) => { + const newResource = resources.joinPath(resource, fileName); + return this.fileService.createFolder(newResource).then(() => this.explorerService.select(newResource, true), (error) => { this.onErrorWithRetry(error, () => this.runAction(fileName)); }); } From 0fdd5ff0a06ec81f5e6ff9a7536bb1e30afbafee Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 20 Dec 2018 17:29:21 +0100 Subject: [PATCH 0047/1066] isVisible -> isBodyVisible --- .../parts/files/electron-browser/views/explorerView.ts | 6 +++--- 1 file changed, 3 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 87603f7e623..9c81f739060 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -333,7 +333,7 @@ export class ExplorerView extends ViewletPanel { * Refresh the contents of the explorer to get up to date data from the disk about the file structure. */ refresh(item?: ExplorerItem): Promise { - if (!this.tree || !this.isVisible()) { + if (!this.tree || !this.isBodyVisible()) { this.shouldRefresh = true; return Promise.resolve(void 0); } @@ -350,7 +350,7 @@ export class ExplorerView extends ViewletPanel { } private setTreeInput(): Promise { - if (!this.isVisible()) { + if (!this.isBodyVisible()) { this.shouldRefresh = true; return Promise.resolve(void 0); } @@ -399,7 +399,7 @@ export class ExplorerView extends ViewletPanel { } private onSelectItem(fileStat: ExplorerItem, reveal = this.autoReveal): Promise { - if (!fileStat || !this.isVisible()) { + if (!fileStat || !this.isBodyVisible()) { return Promise.resolve(void 0); } From 760081d4a691250dc26733c66bb833b6c4a2beee Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Thu, 20 Dec 2018 15:30:55 -0500 Subject: [PATCH 0048/1066] 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 627ab44080253f7dce5e4972d4398b1aa5da4618 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 11:14:23 +0100 Subject: [PATCH 0049/1066] explorer: simplify actions and fix input box --- src/vs/workbench/parts/files/common/files.ts | 3 +- .../files/electron-browser/fileActions.ts | 324 ++++++------------ .../electron-browser/views/explorerView.ts | 8 +- .../electron-browser/views/explorerViewer.ts | 83 +---- 4 files changed, 119 insertions(+), 299 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index f215333699d..ee25b7cea77 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -21,7 +21,6 @@ import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewCon import { Schemas } from 'vs/base/common/network'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; -import { IAction } from 'vs/base/common/actions'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; /** @@ -35,7 +34,7 @@ export const VIEW_CONTAINER: ViewContainer = Registry.as string; - action: IAction; + onFinish: (value: string, success: boolean) => void; } export interface IExplorerService { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 596994384c1..4bbbb5e8804 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -13,8 +13,7 @@ import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import * as strings from 'vs/base/common/strings'; -import { Action, IAction } from 'vs/base/common/actions'; -import { IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; +import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { VIEWLET_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; @@ -46,11 +45,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; - -export interface IEditableData { - action: IAction; - validator: IInputValidator; -} +import { onUnexpectedError } from 'vs/base/common/errors'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -100,145 +95,29 @@ export class BaseErrorReportingAction extends Action { } } -class TriggerRenameFileAction extends BaseErrorReportingAction { +const PLACEHOLDER_URI = URI.file(''); - public static readonly ID = 'renameFile'; - - private renameAction: BaseRenameAction; +/* New File */ +export class NewFileAction extends BaseErrorReportingAction { + static readonly ID = 'workbench.files.action.createFileFromExplorer'; + static readonly LABEL = nls.localize('createNewFile', "New File"); constructor( private element: ExplorerItem, @INotificationService notificationService: INotificationService, - @IInstantiationService instantiationService: IInstantiationService, - @IExplorerService private explorerService: IExplorerService + @IExplorerService private explorerService: IExplorerService, + @IFileService private fileService: IFileService, + @IEditorService private editorService: IEditorService ) { - super(TriggerRenameFileAction.ID, TRIGGER_RENAME_LABEL, notificationService); - - this.element = element; - this.renameAction = instantiationService.createInstance(RenameFileAction, element); + super('explorer.newFile', NEW_FILE_LABEL, notificationService); + this.class = 'explorer-action new-file'; } - public validateFileName(name: string): string { - const names: string[] = coalesce(name.split(/[\\/]/)); - if (names.length > 1) { // error only occurs on multi-path - const comparer = isLinux ? strings.compare : strings.compareIgnoreCase; - if (comparer(names[0], this.element.name) === 0) { - return nls.localize('renameWhenSourcePathIsParentOfTargetError', "Please use the 'New Folder' or 'New File' command to add children to an existing folder"); - } - } - - return this.renameAction.validateFileName(this.element.parent, name); - } - - public run(): Promise { - this.explorerService.setEditable(this.element, { validationMessage: this.validateFileName, action: this.renameAction }); - return Promise.resolve(void 0); - } -} - -export abstract class BaseRenameAction extends BaseErrorReportingAction { - - constructor( - id: string, - label: string, - public element: ExplorerItem, - @INotificationService notificationService: INotificationService, - ) { - super(id, label, notificationService); - this.enabled = this.element && !this.element.isReadonly; - } - - public run(context?: any): Promise { - if (!context) { - return Promise.reject(new Error('No context provided to BaseRenameFileAction.')); - } - - let name = context.value; - if (!name) { - return Promise.reject(new Error('No new name provided to BaseRenameFileAction.')); - } - - // Automatically trim whitespaces and trailing dots to produce nice file names - name = getWellFormedFileName(name); - const existingName = getWellFormedFileName(this.element.name); - - // Return early if name is invalid or didn't change - if (name === existingName || this.validateFileName(this.element.parent, name)) { - return Promise.resolve(null); - } - - // Call function and Emit Event through viewer - const promise = this.runAction(name).then(void 0, (error: any) => { - this.onError(error); - }); - - return promise; - } - - public validateFileName(parent: ExplorerItem, name: string): string { - let source = this.element.name; - let target = name; - - if (!isLinux) { // allow rename of same file also when case differs (e.g. Game.js => game.js) - source = source.toLowerCase(); - target = target.toLowerCase(); - } - - if (getWellFormedFileName(source) === getWellFormedFileName(target)) { - return null; - } - - return validateFileName(parent, name); - } - - public abstract runAction(newName: string): Promise; -} - -class RenameFileAction extends BaseRenameAction { - - public static readonly ID = 'workbench.files.action.renameFile'; - - constructor( - element: ExplorerItem, - @INotificationService notificationService: INotificationService, - @ITextFileService private textFileService: ITextFileService - ) { - super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, notificationService); - } - - public runAction(newName: string): Promise { - const parentResource = this.element.parent.resource; - const targetResource = resources.joinPath(parentResource, newName); - - return this.textFileService.move(this.element.resource, targetResource); - } -} - -/* Base New File/Folder Action */ -export class BaseNewAction extends BaseErrorReportingAction { - private presetFolder: ExplorerItem; - static readonly PLACEHOLDER_URI = URI.file(''); - - constructor( - id: string, - label: string, - private isFile: boolean, - private renameAction: BaseRenameAction, - element: ExplorerItem, - @INotificationService notificationService: INotificationService, - @IExplorerService private explorerService: IExplorerService - ) { - super(id, label, notificationService); - - if (element) { - this.presetFolder = element.isDirectory ? element : element.parent; - } - } - - public run(): Promise { - - let folder = this.presetFolder; - if (!folder) { + run(): Promise { + let folder: ExplorerItem; + if (this.element) { + folder = this.element.isDirectory ? this.element : this.element.parent; + } else { folder = this.explorerService.roots[0]; } @@ -246,40 +125,83 @@ export class BaseNewAction extends BaseErrorReportingAction { return Promise.reject(new Error('Parent folder is readonly.')); } - const stat = new ExplorerItem(BaseNewAction.PLACEHOLDER_URI, folder, !this.isFile); + const stat = new ExplorerItem(PLACEHOLDER_URI, folder, false); folder.addChild(stat); - this.renameAction.element = stat; - this.explorerService.setEditable(stat, { action: this.renameAction, validationMessage: value => this.renameAction.validateFileName(folder, value) }); - return Promise.resolve(void 0); - } -} + const onSuccess = value => { + return this.fileService.createFile(resources.joinPath(folder.resource, value)).then(stat => { + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + }, (error) => { + this.onErrorWithRetry(error, () => onSuccess(value)); + }); + }; -/* New File */ -export class NewFileAction extends BaseNewAction { - constructor( - element: ExplorerItem, - @INotificationService notificationService: INotificationService, - @IInstantiationService instantiationService: IInstantiationService, - @IExplorerService explorerService: IExplorerService - ) { - super('explorer.newFile', NEW_FILE_LABEL, true, instantiationService.createInstance(CreateFileAction, element), null, notificationService, explorerService); - this.class = 'explorer-action new-file'; + this.explorerService.setEditable(stat, { + validationMessage: value => validateFileName(stat, value), + onFinish: (value, success) => { + folder.removeChild(stat); + this.explorerService.setEditable(stat, null); + if (success) { + onSuccess(value); + } + } + }); + + return Promise.resolve(null); } } /* New Folder */ -export class NewFolderAction extends BaseNewAction { +export class NewFolderAction extends BaseErrorReportingAction { + static readonly ID = 'workbench.files.action.createFolderFromExplorer'; + static readonly LABEL = nls.localize('createNewFolder', "New Folder"); constructor( - element: ExplorerItem, + private element: ExplorerItem, @INotificationService notificationService: INotificationService, - @IInstantiationService instantiationService: IInstantiationService, - @IExplorerService explorerService: IExplorerService + @IFileService private fileService: IFileService, + @IExplorerService private explorerService: IExplorerService ) { - super('explorer.newFolder', NEW_FOLDER_LABEL, false, instantiationService.createInstance(CreateFolderAction, element), null, notificationService, explorerService); + super('explorer.newFolder', NEW_FOLDER_LABEL, notificationService); this.class = 'explorer-action new-folder'; } + + run(): Promise { + let folder: ExplorerItem; + if (this.element) { + folder = this.element.isDirectory ? this.element : this.element.parent; + } else { + folder = this.explorerService.roots[0]; + } + + if (folder.isReadonly) { + return Promise.reject(new Error('Parent folder is readonly.')); + } + + const stat = new ExplorerItem(PLACEHOLDER_URI, folder, true); + folder.addChild(stat); + + const onSuccess = value => { + return this.fileService.createFolder(resources.joinPath(folder.resource, value)).then(stat => { + return this.explorerService.select(stat.resource, true); + }, (error) => { + this.onErrorWithRetry(error, () => onSuccess(value)); + }); + }; + + this.explorerService.setEditable(stat, { + validationMessage: value => validateFileName(stat, value), + onFinish: (value, success) => { + folder.removeChild(stat); + this.explorerService.setEditable(stat, null); + if (success) { + onSuccess(value); + } + } + }); + + return Promise.resolve(null); + } } /* Create new file from anywhere: Open untitled */ @@ -300,57 +222,6 @@ export class GlobalNewUntitledFileAction extends Action { } } -/* Create New File (only used internally by explorerViewer) */ -class CreateFileAction extends BaseRenameAction { - - public static readonly ID = 'workbench.files.action.createFileFromExplorer'; - public static readonly LABEL = nls.localize('createNewFile', "New File"); - - constructor( - element: ExplorerItem, - @IEditorService private editorService: IEditorService, - @IFileService private fileService: IFileService, - @INotificationService notificationService: INotificationService, - ) { - super(CreateFileAction.ID, CreateFileAction.LABEL, element, notificationService); - } - - public runAction(fileName: string): Promise { - const resource = this.element.parent.resource; - this.element.parent.removeChild(this.element); - return this.fileService.createFile(resources.joinPath(resource, fileName)).then(stat => { - return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - }, (error) => { - this.onErrorWithRetry(error, () => this.runAction(fileName)); - }); - } -} - -/* Create New Folder (only used internally by explorerViewer) */ -class CreateFolderAction extends BaseRenameAction { - - public static readonly ID = 'workbench.files.action.createFolderFromExplorer'; - public static readonly LABEL = nls.localize('createNewFolder', "New Folder"); - - constructor( - element: ExplorerItem, - @IFileService private fileService: IFileService, - @INotificationService notificationService: INotificationService, - @IExplorerService private explorerService: IExplorerService - ) { - super(CreateFolderAction.ID, CreateFolderAction.LABEL, element, notificationService); - } - - public runAction(fileName: string): Promise { - const resource = this.element.parent.resource; - this.element.parent.removeChild(this.element); - const newResource = resources.joinPath(resource, fileName); - return this.fileService.createFolder(newResource).then(() => this.explorerService.select(newResource, true), (error) => { - this.onErrorWithRetry(error, () => this.runAction(fileName)); - }); - } -} - class BaseDeleteFileAction extends BaseErrorReportingAction { private static readonly CONFIRM_DELETE_SETTING_KEY = 'explorer.confirmDelete'; @@ -563,7 +434,6 @@ class BaseDeleteFileAction extends BaseErrorReportingAction { /* Add File */ export class AddFilesAction extends BaseErrorReportingAction { - constructor( private element: ExplorerItem, clazz: string, @@ -1125,8 +995,7 @@ export class ShowOpenedFileInNewWindow extends Action { } } -export function validateFileName(parent: ExplorerItem, name: string): string { - +export function validateFileName(item: ExplorerItem, name: string): string { // Produce a well formed file name name = getWellFormedFileName(name); @@ -1141,11 +1010,14 @@ export function validateFileName(parent: ExplorerItem, name: string): string { } const names = coalesce(name.split(/[\\/]/)); + const parent = item.parent; - // Do not allow to overwrite existing file - const childExists = !!parent.getChild(name); - if (childExists) { - return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name); + if (name !== item.name) { + // Do not allow to overwrite existing file + const childExists = !!parent.getChild(name); + if (childExists) { + return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name); + } } // Invalid File name @@ -1309,12 +1181,22 @@ CommandsRegistry.registerCommand({ }); export const renameHandler = (accessor: ServicesAccessor) => { - const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); - const explorerContext = getContext(listService.lastFocusedList); + const explorerService = accessor.get(IExplorerService); + const textFileService = accessor.get(ITextFileService); + const { stat } = getContext(listService.lastFocusedList); - const renameAction = instantationService.createInstance(TriggerRenameFileAction, explorerContext.stat); - return renameAction.run(); + explorerService.setEditable(stat, { + validationMessage: value => validateFileName(stat, value), + onFinish: (value, success) => { + if (success) { + const parentResource = stat.parent.resource; + const targetResource = resources.joinPath(parentResource, value); + textFileService.move(stat.resource, targetResource).then(undefined, onUnexpectedError); + } + explorerService.setEditable(stat, null); + } + }); }; export const moveFileToTrashHandler = (accessor: ServicesAccessor) => { 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 9c81f739060..bf19c48ec8e 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -158,7 +158,13 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput())); this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); - this.disposables.push(this.explorerService.onDidChangeEditable(e => this.refresh(e.parent))); + this.disposables.push(this.explorerService.onDidChangeEditable(e => { + let expandPromise = Promise.resolve(null); + if (this.explorerService.getEditableData(e)) { + expandPromise = this.tree.expand(e.parent); + } + expandPromise.then(() => this.refresh(e.parent)); + })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); // Update configuration 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 aa38bd07bc4..883dadb602d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -11,7 +11,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IFileService, FileKind } from 'vs/platform/files/common/files'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IFileLabelOptions, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; @@ -19,18 +19,16 @@ import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration, IExplorerService } from 'vs/workbench/parts/files/common/files'; -import { dirname, joinPath, basename } from 'vs/base/common/resources'; +import { IFilesConfiguration, IExplorerService, IEditableData } from 'vs/workbench/parts/files/common/files'; +import { dirname, joinPath } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { once } from 'vs/base/common/functional'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { normalize, join, nativeSep } from 'vs/base/common/paths'; -import { rtrim } from 'vs/base/common/strings'; +import { normalize } from 'vs/base/common/paths'; import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; -import { IAction } from 'vs/base/common/actions'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { compareFileExtensions, compareFileNames } from 'vs/base/common/comparers'; @@ -96,7 +94,6 @@ export class FilesRenderer implements ITreeRenderer(); @@ -148,7 +145,7 @@ export class FilesRenderer implements ITreeRenderer string, action: IAction }): void { + private renderInputBox(container: HTMLElement, stat: ExplorerItem, editableData: IEditableData): void { // Use a file label only for the icon next to the input box const label = this.labels.create(container); @@ -191,18 +188,12 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); inputBox.focus(); - const done = once(async (commit: boolean, blur: boolean) => { + const done = once(async (success: boolean, blur: boolean) => { label.element.style.display = 'none'; - - if (!commit) { - stat.parent.removeChild(stat); - } - if (commit && inputBox.value) { - await editableData.action.run({ value: inputBox.value }); - } + const value = inputBox.value; dispose(toDispose); container.removeChild(label.element); - this.explorerService.setEditable(stat, null); + editableData.onFinish(value, success); }); const toDispose = [ @@ -216,14 +207,6 @@ export class FilesRenderer implements ITreeRenderer { - const initialRelPath: string = path.relative(stat.root.resource.path, stat.parent.resource.path); - let projectFolderName: string = ''; - if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { - projectFolderName = basename(stat.root.resource); // show root folder name in multi-folder project - } - this.showInputMessage(inputBox, initialRelPath, projectFolderName, editableData.action.id); - }), DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => { done(inputBox.isInputValid(), true); }), @@ -232,47 +215,6 @@ export class FilesRenderer implements ITreeRenderer, index: number, templateData: IFileTemplateData): void { // noop } @@ -419,15 +361,6 @@ export class FileSorter implements ITreeSorter { break; } - // Sort "New File/Folder" placeholders - if (this.explorerService.getEditableData(statA)) { - return -1; - } - - if (this.explorerService.getEditableData(statB)) { - return 1; - } - // Sort Files switch (sortOrder) { case 'type': From 35a5ebdf32a09044694ecb521b1fcb3e36f10fbc Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 11:27:26 +0100 Subject: [PATCH 0050/1066] explorer: do not cache children, tree will do that for you --- .../workbench/parts/files/common/explorerModel.ts | 14 ++++---------- .../files/electron-browser/views/explorerView.ts | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index e5a7487021e..9b90f950648 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -247,16 +247,10 @@ export class ExplorerItem { } fetchChildren(fileService: IFileService): Promise { - let promise = Promise.resolve(null); - 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; - }); - } - - return promise.then(() => { + return fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { + const resolved = ExplorerItem.create(stat, this); + ExplorerItem.mergeLocalWithDisk(resolved, this); + this.isDirectoryResolved = true; const items: ExplorerItem[] = []; this.children.forEach(child => { items.push(child); 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 bf19c48ec8e..aa6bb922629 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -161,7 +161,7 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.explorerService.onDidChangeEditable(e => { let expandPromise = Promise.resolve(null); if (this.explorerService.getEditableData(e)) { - expandPromise = this.tree.expand(e.parent); + expandPromise = ignoreErrors(this.tree.expand(e.parent)); } expandPromise.then(() => this.refresh(e.parent)); })); From 8151464a404796794f94aac40b5646b73ea45d22 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 11:34:53 +0100 Subject: [PATCH 0051/1066] fix multi selection context computation to work with new tree --- src/vs/workbench/parts/files/browser/files.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index 93bf737781c..f745fc4a024 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -4,10 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IListService } from 'vs/platform/list/browser/listService'; +import { IListService, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { OpenEditor } from 'vs/workbench/parts/files/common/files'; import { toResource } from 'vs/workbench/common/editor'; -import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; @@ -45,7 +44,7 @@ export function getMultiSelectedResources(resource: URI | object, listService: I const list = listService.lastFocusedList; if (list && list.getHTMLElement() === document.activeElement) { // Explorer - if (list instanceof Tree) { + if (list instanceof WorkbenchAsyncDataTree) { const selection = list.getSelection().map((fs: ExplorerItem) => fs.resource); const focus = list.getFocus(); const mainUriStr = URI.isUri(resource) ? resource.toString() : focus instanceof ExplorerItem ? focus.resource.toString() : undefined; From dacc2355d2bab35f21791efdcea227f0d0a3a824 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 11:48:47 +0100 Subject: [PATCH 0052/1066] explorer: ignore active editor change events produced by explorer --- .../parts/files/electron-browser/views/explorerView.ts | 10 ++++++++-- 1 file changed, 8 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 aa6bb922629..85f54821f5e 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -59,6 +59,7 @@ export class ExplorerView extends ViewletPanel { private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; + private ignoreActiveEditorChange; constructor( options: IViewletPanelOptions, @@ -173,7 +174,7 @@ export class ExplorerView extends ViewletPanel { // When the explorer viewer is loaded, listen to changes to the editor input this.disposables.push(this.editorService.onDidActiveEditorChange(() => { - if (this.autoReveal) { + if (this.autoReveal && !this.ignoreActiveEditorChange) { const activeFile = this.getActiveFile(); if (activeFile) { this.explorerService.select(this.getActiveFile()); @@ -278,7 +279,12 @@ export class ExplorerView extends ViewletPanel { "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } }*/ this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.ignoreActiveEditorChange = true; + this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP) + .then(() => this.ignoreActiveEditorChange = false).catch(e => { + this.ignoreActiveEditorChange = false; + onUnexpectedError(e); + }); } } })); From 41b0bf63bea9503c1fd2d98abbed7e673fa6f9a6 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 12:17:10 +0100 Subject: [PATCH 0053/1066] explorer: smarter resolving of children stats with cache --- .../workbench/parts/files/common/explorerModel.ts | 14 ++++++++++---- .../files/electron-browser/explorerService.ts | 10 +++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 9b90f950648..e5a7487021e 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -247,10 +247,16 @@ export class ExplorerItem { } fetchChildren(fileService: IFileService): Promise { - return fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { - const resolved = ExplorerItem.create(stat, this); - ExplorerItem.mergeLocalWithDisk(resolved, this); - this.isDirectoryResolved = true; + let promise = Promise.resolve(null); + 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; + }); + } + + return promise.then(() => { const items: ExplorerItem[] = []; this.children.forEach(child => { items.push(child); diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 9af0b4eb53b..765d8573a5f 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -220,6 +220,10 @@ export class ExplorerService implements IExplorerService { setTimeout(() => { // Filter to the ones we care e = this.filterToViewRelevantEvents(e); + const explorerItemChanged = (item: ExplorerItem) => { + item.isDirectoryResolved = false; + this._onDidChangeItem.fire(item); + }; // Handle added files/folders const added = e.getAdded(); @@ -241,7 +245,7 @@ export class ExplorerService implements IExplorerService { // Compute if parent is visible and added file not yet part of it const parentStat = this.model.findClosest(parent); if (parentStat && parentStat.isDirectoryResolved && !this.model.findClosest(change.resource)) { - this._onDidChangeItem.fire(parentStat); + explorerItemChanged(parentStat); } // Keep track of path that can be ignored for faster lookup @@ -260,7 +264,7 @@ export class ExplorerService implements IExplorerService { const del = deleted[j]; const item = this.model.findClosest(del.resource); if (item) { - this._onDidChangeItem.fire(item.parent); + explorerItemChanged(item.parent); } } } @@ -275,7 +279,7 @@ export class ExplorerService implements IExplorerService { const item = this.model.findClosest(upd.resource); if (item) { - this._onDidChangeItem.fire(item.parent); + explorerItemChanged(item.parent); } } } From 834eec91f03864054492ccd2f2bbf8c6e9932c7c Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 21 Dec 2018 12:40:07 +0100 Subject: [PATCH 0054/1066] explorer: error decorations for broken root --- src/vs/workbench/parts/files/common/explorerModel.ts | 12 ++++-------- .../parts/files/electron-browser/explorerService.ts | 11 ++++++----- .../views/explorerDecorationsProvider.ts | 3 +++ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index e5a7487021e..36217b1deb4 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -74,6 +74,7 @@ export class ExplorerModel implements IDisposable { export class ExplorerItem { public isDirectoryResolved: boolean; + public isError: boolean; constructor( public resource: URI, @@ -84,7 +85,6 @@ export class ExplorerItem { private _name: string = resources.basenameOrAuthority(resource), private _mtime?: number, private _etag?: string, - private _isError?: boolean ) { this.isDirectoryResolved = false; } @@ -109,10 +109,6 @@ export class ExplorerItem { return this._mtime; } - get isError(): boolean { - return !!this._isError; - } - get name(): string { return this._name; } @@ -152,8 +148,8 @@ export class ExplorerItem { return this === this.root; } - static create(raw: IFileStat, parent: ExplorerItem, resolveTo?: URI[], isError = false): ExplorerItem { - const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag, isError); + static create(raw: IFileStat, parent: ExplorerItem, resolveTo?: URI[]): ExplorerItem { + const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag); // Recursively add children if present if (stat.isDirectory) { @@ -201,7 +197,7 @@ export class ExplorerItem { local.isDirectoryResolved = disk.isDirectoryResolved; local._isSymbolicLink = disk.isSymbolicLink; local._isReadonly = disk.isReadonly; - local._isError = disk.isError; + local.isError = disk.isError; // Merge Children if resolved if (mergingDirectories && disk.isDirectoryResolved) { diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 765d8573a5f..e459bba9f83 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -16,7 +16,6 @@ import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IExpression } from 'vs/base/common/glob'; -import { INotificationService } from 'vs/platform/notification/common/notification'; function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { const scope = root ? { resource: root } : void 0; @@ -42,8 +41,7 @@ export class ExplorerService implements IExplorerService { @IFileService private fileService: IFileService, @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService, - @INotificationService private notificationService: INotificationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { } get roots(): ExplorerItem[] { @@ -120,17 +118,20 @@ export class ExplorerService implements IExplorerService { const options: IResolveFileOptions = { resolveTo: [resource] }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); return this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model - const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); const modelStat = ExplorerItem.create(stat, null, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); // Select and Reveal this._onDidSelectItem.fire({ item: root.find(resource), reveal }); - }, e => { this.notificationService.error(e); }); + }, () => { + root.isError = true; + this._onDidChangeItem.fire(root); + }); } // File events diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts index e4fc759afaa..402ae0ec2a5 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts @@ -26,6 +26,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider { this.toDispose.push(contextService.onDidChangeWorkspaceFolders(e => { this._onDidChange.fire(e.changed.concat(e.added).map(wf => wf.uri)); })); + this.toDispose.push(explorerService.onDidChangeItem(item => { + this._onDidChange.fire([item.resource]); + })); } get onDidChange(): Event { From 4c02c554ce5880822c4f5bcdc999466201d50850 Mon Sep 17 00:00:00 2001 From: Krish De Souza Date: Sat, 22 Dec 2018 09:39:52 +0530 Subject: [PATCH 0055/1066] 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 00e4a138708feaf22301c5ffea9871c1fa48eef8 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 27 Dec 2018 16:46:03 +0100 Subject: [PATCH 0056/1066] explorerView: multipleSelectionSupport --- .../workbench/parts/files/electron-browser/views/explorerView.ts | 1 + 1 file changed, 1 insertion(+) 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 85f54821f5e..3645830b320 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -232,6 +232,7 @@ export class ExplorerView extends ViewletPanel { keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: stat => stat.name }, + multipleSelectionSupport: true, filter: this.filter, sorter: this.instantiationService.createInstance(FileSorter) }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); From 5b06dcc20c7f13bad76a6c98a6d89461b4a002de Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 27 Dec 2018 16:54:33 +0100 Subject: [PATCH 0057/1066] fix strick null errors --- .../parts/files/common/explorerModel.ts | 28 +++++++++++-------- .../views/explorerDecorationsProvider.ts | 4 ++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 36217b1deb4..e0915b71044 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -78,7 +78,7 @@ export class ExplorerItem { constructor( public resource: URI, - private _parent: ExplorerItem, + private _parent: ExplorerItem | undefined, private _isDirectory?: boolean, private _isSymbolicLink?: boolean, private _isReadonly?: boolean, @@ -101,11 +101,11 @@ export class ExplorerItem { return !!this._isReadonly; } - get etag(): string { + get etag(): string | undefined { return this._etag; } - get mtime(): number { + get mtime(): number | undefined { return this._mtime; } @@ -113,7 +113,7 @@ export class ExplorerItem { return this._name; } - get parent(): ExplorerItem { + get parent(): ExplorerItem | undefined { return this._parent; } @@ -122,7 +122,7 @@ export class ExplorerItem { return this; } - return this.parent.root; + return this._parent.root; } @memoize get children(): Map { @@ -131,12 +131,12 @@ export class ExplorerItem { private updateName(value: string): void { // Re-add to parent since the parent has a name map to children and the name might have changed - if (this.parent) { - this.parent.removeChild(this); + if (this._parent) { + this._parent.removeChild(this); } this._name = value; - if (this.parent) { - this.parent.addChild(this); + if (this._parent) { + this._parent.addChild(this); } } @@ -243,7 +243,7 @@ export class ExplorerItem { } fetchChildren(fileService: IFileService): Promise { - let promise = Promise.resolve(null); + let promise: Promise = Promise.resolve(undefined); if (!this.isDirectoryResolved) { promise = fileService.resolveFile(this.resource, { resolveSingleChildDescendants: true }).then(stat => { const resolved = ExplorerItem.create(stat, this); @@ -277,14 +277,18 @@ export class ExplorerItem { * Moves this element under a new parent element. */ move(newParent: ExplorerItem): void { - this.parent.removeChild(this); + if (this._parent) { + this._parent.removeChild(this); + } newParent.removeChild(this); // make sure to remove any previous version of the file if any newParent.addChild(this); this.updateResource(true); } private updateResource(recursive: boolean): void { - this.resource = resources.joinPath(this.parent.resource, this.name); + if (this._parent) { + this.resource = resources.joinPath(this._parent.resource, this.name); + } if (recursive) { if (this.isDirectory) { diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts index 402ae0ec2a5..46cc8f53195 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts @@ -27,7 +27,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider { this._onDidChange.fire(e.changed.concat(e.added).map(wf => wf.uri)); })); this.toDispose.push(explorerService.onDidChangeItem(item => { - this._onDidChange.fire([item.resource]); + if (item) { + this._onDidChange.fire([item.resource]); + } })); } From 8205d0b8b2114d89a4c4f3910d3967ddbc9e196d Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 28 Dec 2018 15:44:57 +0100 Subject: [PATCH 0058/1066] fix unit tests --- .../parts/files/electron-browser/fileActions.ts | 2 +- .../test/electron-browser/explorerModel.test.ts | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 4bbbb5e8804..12a1f4792f0 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1014,7 +1014,7 @@ export function validateFileName(item: ExplorerItem, name: string): string { if (name !== item.name) { // Do not allow to overwrite existing file - const childExists = !!parent.getChild(name); + const childExists = parent && !!parent.getChild(name); if (childExists) { return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name); } 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 e37d12d7ce6..a5f20068a38 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 @@ -202,8 +202,7 @@ suite('Files - View Model', () => { assert(validateFileName(s, 'foo>bar') !== null); assert(validateFileName(s, 'foo|bar') !== null); } - assert(validateFileName(s, 'alles.klar') !== null); - + assert(validateFileName(s, 'alles.klar') === null); assert(validateFileName(s, '.foo') === null); assert(validateFileName(s, 'foo.bar') === null); assert(validateFileName(s, 'foo') === null); @@ -215,15 +214,10 @@ suite('Files - View Model', () => { const sChild = createStat('/path/to/stat/alles.klar', 'alles.klar', true, true, 8096, d); s.addChild(sChild); - assert(validateFileName(s, 'alles.klar') !== null); + assert(validateFileName(s, 'alles.klar') === null); - if (isLinux) { - assert(validateFileName(s, 'Alles.klar') === null); - assert(validateFileName(s, 'Alles.Klar') === null); - } else { - assert(validateFileName(s, 'Alles.klar') !== null); - assert(validateFileName(s, 'Alles.Klar') !== null); - } + assert(validateFileName(s, 'Alles.klar') === null); + assert(validateFileName(s, 'Alles.Klar') === null); assert(validateFileName(s, '.foo') === null); assert(validateFileName(s, 'foo.bar') === null); From 8e8cdc2602eb8f1ece7a1f9b5278803ce71d7d90 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Wed, 2 Jan 2019 15:21:43 -0700 Subject: [PATCH 0059/1066] Re-enable link detection (fixes #34026) --- .../parts/debug/browser/exceptionWidget.ts | 18 +++++++++++++-- .../parts/debug/browser/linkDetector.ts | 23 ++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts index 5dc601d2784..990d2219c1e 100644 --- a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts @@ -80,8 +80,22 @@ export class ExceptionWidget extends ZoneWidget { if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) { let stackTrace = $('.stack-trace'); const linkDetector = this.instantiationService.createInstance(LinkDetector); - const linkedStackTrace = linkDetector.handleLinks(this.exceptionInfo.details.stackTrace); - typeof linkedStackTrace === 'string' ? stackTrace.textContent = linkedStackTrace : stackTrace.appendChild(linkedStackTrace); + + const stackTraceContainer = document.createElement('span'); + + // Detect links one line at a time so we don't go over the detector's character limit + const lines = this.exceptionInfo.details.stackTrace.split('\n'); + lines.forEach((line, index) => { + let linkedLine = linkDetector.handleLinks(line + '\n'); + if (typeof linkedLine === 'string') { + const span = document.createElement('span'); + span.textContent = linkedLine; + linkedLine = span; + } + stackTraceContainer.appendChild(linkedLine); + }); + + stackTrace.appendChild(stackTraceContainer); dom.append(container, stackTrace); } } diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index a795c92dbc2..ba81ea80016 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { isAbsolute } from 'vs/base/common/paths'; import { URI as uri } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -28,14 +29,18 @@ export class LinkDetector { } /** - * Matches and handles relative and absolute file links in the string provided. + * Matches and handles absolute file links in the string provided. * Returns element that wraps the processed string, where matched links are replaced by and unmatched parts are surrounded by elements. * 'onclick' event is attached to all anchored links that opens them in the editor. * If no links were detected, returns the original string. */ handleLinks(text: string): HTMLElement | string { + let unalteredPostfix = ''; if (text.length > LinkDetector.MAX_LENGTH) { - return text; + // For performance, only handle the first MAX_LENGTH characters (without cutting off in the middle of a line) + const cutoffIndex = text.lastIndexOf('\n', LinkDetector.MAX_LENGTH) + 1; + unalteredPostfix = text.substr(cutoffIndex); + text = text.substr(0, cutoffIndex); } let linkContainer: HTMLElement | undefined; @@ -45,7 +50,8 @@ export class LinkDetector { let match = pattern.exec(text); while (match !== null) { - let resource: uri | null = null; + let resource: uri | null = isAbsolute(match[1]) ? uri.file(match[1]) : null; + if (!resource) { match = pattern.exec(text); continue; @@ -85,6 +91,17 @@ export class LinkDetector { } } + // append the text that went over the MAX_LENGTH limit + if (unalteredPostfix) { + if (linkContainer) { + let span = document.createElement('span'); + span.textContent = unalteredPostfix; + linkContainer.appendChild(span); + } else { + text += unalteredPostfix; + } + } + return linkContainer || text; } From 1ef045875d6964d0f0e2b241e086c30f28889bc8 Mon Sep 17 00:00:00 2001 From: pkoushik Date: Thu, 3 Jan 2019 11:56:11 +0530 Subject: [PATCH 0060/1066] fix-65575 Added CachedToken and implementation to use it --- .../markdown-language-features/src/markdownEngine.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 9ad45ea3dd2..f88af143b3b 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -20,6 +20,8 @@ export class MarkdownEngine { private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map(); + private cachedToken = new Set(); + public constructor( private readonly extensionPreviewResourceProvider: MarkdownContributions, @@ -107,6 +109,11 @@ export class MarkdownEngine { this._slugCount = new Map(); const engine = await this.getEngine(document.uri); + if (this.cachedToken) { + return engine.renderer.render([...this.cachedToken].map(token => { + return token; + }), this.md, {}); + } return engine.render(text); } @@ -123,6 +130,7 @@ export class MarkdownEngine { token.map[0] += offset; token.map[1] += offset; } + this.cachedToken.add(token); return token; }); } From 39d179920bd503a445c57791705e71d7dd6339c4 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 3 Jan 2019 15:42:28 +0100 Subject: [PATCH 0061/1066] explorer: polish rename box, draw highlight --- .../parts/files/electron-browser/media/explorerviewlet.css | 5 +++++ .../parts/files/electron-browser/views/explorerView.ts | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) 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 612f6733d86..a38a2026ee5 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -14,6 +14,11 @@ height: 100%; } +.explorer-viewlet .monaco-list.highlight .explorer-item:not(.explorer-item-edited), +.explorer-viewlet .monaco-list.highlight .monaco-tl-twistie { + opacity: 0.3; +} + .explorer-viewlet .explorer-item, .explorer-viewlet .open-editor, .explorer-viewlet .editor-group { 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 3645830b320..2627a640f12 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -161,9 +161,12 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); this.disposables.push(this.explorerService.onDidChangeEditable(e => { let expandPromise = Promise.resolve(null); - if (this.explorerService.getEditableData(e)) { + const isEditing = !!this.explorerService.getEditableData(e); + if (isEditing) { + this.tree.setFocus([]); expandPromise = ignoreErrors(this.tree.expand(e.parent)); } + DOM.toggleClass(this.tree.getHTMLElement(), 'highlight', isEditing); expandPromise.then(() => this.refresh(e.parent)); })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); From 5325e2bfe6180a53c764b7fb4027c792db6cbb4a Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 3 Jan 2019 15:56:46 +0100 Subject: [PATCH 0062/1066] explorer: do not react if user is clicking on directories or explorer items which are input placeholders --- .../electron-browser/views/explorerView.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 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 2627a640f12..790b3096df9 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -265,6 +265,10 @@ export class ExplorerView extends ViewletPanel { const selection = e.elements; // Do not react if the user is expanding selection if (selection && selection.length === 1) { + if (selection[0].isDirectory || !selection[0].name) { + // Do not react if user is clicking on directories or explorer items which are input placeholders + return; + } let isDoubleClick = false; let sideBySide = false; let isMiddleClick = false; @@ -275,21 +279,19 @@ export class ExplorerView extends ViewletPanel { sideBySide = this.tree.useAltAsMultipleSelectionModifier ? (e.browserEvent.ctrlKey || e.browserEvent.metaKey) : e.browserEvent.altKey; } - if (!selection[0].isDirectory) { - // Pass focus for keyboard events and for double click - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - }*/ - this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); - this.ignoreActiveEditorChange = true; - this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP) - .then(() => this.ignoreActiveEditorChange = false).catch(e => { - this.ignoreActiveEditorChange = false; - onUnexpectedError(e); - }); - } + // Pass focus for keyboard events and for double click + /* __GDPR__ + "workbenchActionExecuted" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + }*/ + this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); + this.ignoreActiveEditorChange = true; + this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP) + .then(() => this.ignoreActiveEditorChange = false).catch(e => { + this.ignoreActiveEditorChange = false; + onUnexpectedError(e); + }); } })); From 661d1916386af827719165e8b675247e44f7ddb6 Mon Sep 17 00:00:00 2001 From: pkoushik Date: Fri, 4 Jan 2019 20:07:21 +0530 Subject: [PATCH 0063/1066] fix-65575 tokenize method added --- .../src/markdownEngine.ts | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index f88af143b3b..06df4598b6a 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -13,14 +13,15 @@ import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; - export class MarkdownEngine { private md?: MarkdownIt; private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map(); - private cachedToken = new Set(); + private cachedTokens = new Map(); + + public constructor( @@ -96,6 +97,24 @@ export class MarkdownEngine { return { text, offset }; } + private tokenize(document: SkinnyTextDocument, engine?: MarkdownIt): Token[] { + const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; + const { text, offset } = this.stripFrontmatter(document.getText()); + let tokens: Token[]; + if (this.cachedTokens.has(document.uri)) { + tokens = this.cachedTokens.get(document.uri)!; + } + tokens = engine!.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { + if (token.map) { + token.map[0] += offset; + token.map[1] += offset; + } + return token; + }); + this.cachedTokens.set(document.uri, tokens); + return tokens; + } + public async render(document: SkinnyTextDocument, stripFrontmatter: boolean): Promise { let offset = 0; let text = document.getText(); @@ -109,30 +128,25 @@ export class MarkdownEngine { this._slugCount = new Map(); const engine = await this.getEngine(document.uri); - if (this.cachedToken) { - return engine.renderer.render([...this.cachedToken].map(token => { - return token; - }), this.md, {}); + let tokens: Token[]; + tokens = this.tokenize(document); + if (!tokens) { + tokens = this.tokenize(document, engine); } - return engine.render(text); + return engine.renderer.render(tokens, this.md, {}); } public async parse(document: SkinnyTextDocument): Promise { - const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; - const { text, offset } = this.stripFrontmatter(document.getText()); this.currentDocument = document.uri; this._slugCount = new Map(); const engine = await this.getEngine(document.uri); - - return engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { - if (token.map) { - token.map[0] += offset; - token.map[1] += offset; - } - this.cachedToken.add(token); - return token; - }); + let tokens: Token[]; + tokens = this.tokenize(document); + if (!tokens) { + tokens = this.tokenize(document, engine); + } + return tokens; } private addLineNumberRenderer(md: any, ruleName: string): void { From eabf436c073fe580061cad4a826918e30e765348 Mon Sep 17 00:00:00 2001 From: pkoushik Date: Fri, 4 Jan 2019 20:13:27 +0530 Subject: [PATCH 0064/1066] fix-65575 tokenize method cache update bug fixed --- extensions/markdown-language-features/src/markdownEngine.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 06df4598b6a..651770b282a 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -100,11 +100,10 @@ export class MarkdownEngine { private tokenize(document: SkinnyTextDocument, engine?: MarkdownIt): Token[] { const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; const { text, offset } = this.stripFrontmatter(document.getText()); - let tokens: Token[]; if (this.cachedTokens.has(document.uri)) { - tokens = this.cachedTokens.get(document.uri)!; + return this.cachedTokens.get(document.uri)!; } - tokens = engine!.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { + const tokens = engine!.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { if (token.map) { token.map[0] += offset; token.map[1] += offset; From 693a021c95943fea601153a149f88e83dacd0d5d Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Fri, 4 Jan 2019 17:38:03 -0700 Subject: [PATCH 0065/1066] Simplifying consumers of LinkDetector LinkDetector now always returns an HTMLElement. Each line of the input is handled separately and is not checked for links if it is longer than MAX_LENGTH, but the rest of the input is still checked. Recursion is used to handle future cases when more RegEx patterns are added. --- .../parts/debug/browser/debugANSIHandling.ts | 10 +- .../parts/debug/browser/exceptionWidget.ts | 18 +-- .../parts/debug/browser/linkDetector.ts | 123 ++++++++++-------- 3 files changed, 72 insertions(+), 79 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts b/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts index 6c09cbd9899..b373c452689 100644 --- a/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/parts/debug/browser/debugANSIHandling.ts @@ -120,15 +120,7 @@ export function appendStylizedStringToContainer(root: HTMLElement, stringContent return; } - const content = linkDetector.handleLinks(stringContent); - let container: HTMLElement; - - if (typeof content === 'string') { - container = document.createElement('span'); - container.textContent = content; - } else { - container = content; - } + const container = linkDetector.handleLinks(stringContent); container.className = cssClasses.join(' '); root.appendChild(container); diff --git a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts index 990d2219c1e..57227eda4ec 100644 --- a/src/vs/workbench/parts/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/parts/debug/browser/exceptionWidget.ts @@ -80,22 +80,8 @@ export class ExceptionWidget extends ZoneWidget { if (this.exceptionInfo.details && this.exceptionInfo.details.stackTrace) { let stackTrace = $('.stack-trace'); const linkDetector = this.instantiationService.createInstance(LinkDetector); - - const stackTraceContainer = document.createElement('span'); - - // Detect links one line at a time so we don't go over the detector's character limit - const lines = this.exceptionInfo.details.stackTrace.split('\n'); - lines.forEach((line, index) => { - let linkedLine = linkDetector.handleLinks(line + '\n'); - if (typeof linkedLine === 'string') { - const span = document.createElement('span'); - span.textContent = linkedLine; - linkedLine = span; - } - stackTraceContainer.appendChild(linkedLine); - }); - - stackTrace.appendChild(stackTraceContainer); + const linkedStackTrace = linkDetector.handleLinks(this.exceptionInfo.details.stackTrace); + stackTrace.appendChild(linkedStackTrace); dom.append(container, stackTrace); } } diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index ba81ea80016..881f4c7672b 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -32,77 +32,92 @@ export class LinkDetector { * Matches and handles absolute file links in the string provided. * Returns element that wraps the processed string, where matched links are replaced by and unmatched parts are surrounded by elements. * 'onclick' event is attached to all anchored links that opens them in the editor. - * If no links were detected, returns the original string. + * If no links were detected, returns the original string wrapped in a . */ - handleLinks(text: string): HTMLElement | string { - let unalteredPostfix = ''; - if (text.length > LinkDetector.MAX_LENGTH) { - // For performance, only handle the first MAX_LENGTH characters (without cutting off in the middle of a line) - const cutoffIndex = text.lastIndexOf('\n', LinkDetector.MAX_LENGTH) + 1; - unalteredPostfix = text.substr(cutoffIndex); - text = text.substr(0, cutoffIndex); + handleLinks(text: string): HTMLElement { + const container = document.createElement('span'); + + // Handle the text one line at a time + const lines = text.split('\n'); + + if (text.charAt(text.length - 1) === '\n') { + // Remove the last element ('') that split added + lines.pop(); } - let linkContainer: HTMLElement | undefined; - for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) { - pattern.lastIndex = 0; // the holy grail of software development - let lastMatchIndex = 0; + for (let i = 0; i < lines.length; i++) { + let line = lines[i]; - let match = pattern.exec(text); - while (match !== null) { - let resource: uri | null = isAbsolute(match[1]) ? uri.file(match[1]) : null; + // Re-introduce the newline for every line except the last (unless the last line originally ended with a newline) + if (i < lines.length - 1 || text.charAt(text.length - 1) === '\n') { + line += '\n'; + } - if (!resource) { - match = pattern.exec(text); - continue; - } - if (!linkContainer) { - linkContainer = document.createElement('span'); - } + // Don't handle links for lines that are too long + if (line.length > LinkDetector.MAX_LENGTH) { + let span = document.createElement('span'); + span.textContent = line; + container.appendChild(span); + continue; + } - let textBeforeLink = text.substring(lastMatchIndex, match.index); - if (textBeforeLink) { - let span = document.createElement('span'); - span.textContent = textBeforeLink; - linkContainer.appendChild(span); - } + const lineContainer = document.createElement('span'); - const link = document.createElement('a'); - link.textContent = text.substr(match.index, match[0].length); - link.title = isMacintosh ? nls.localize('fileLinkMac', "Click to follow (Cmd + click opens to the side)") : nls.localize('fileLink', "Click to follow (Ctrl + click opens to the side)"); - linkContainer.appendChild(link); - const line = Number(match[3]); - const column = match[4] ? Number(match[4]) : undefined; - link.onclick = (e) => this.onLinkClick(new StandardMouseEvent(e), resource!, line, column); + for (let pattern of LinkDetector.FILE_LOCATION_PATTERNS) { + // Reset the state of the pattern + pattern = new RegExp(pattern); + let lastMatchIndex = 0; - lastMatchIndex = pattern.lastIndex; - const currentMatch = match; - match = pattern.exec(text); + let match = pattern.exec(line); - // Append last string part if no more link matches - if (!match) { - let textAfterLink = text.substr(currentMatch.index + currentMatch[0].length); - if (textAfterLink) { - let span = document.createElement('span'); - span.textContent = textAfterLink; - linkContainer.appendChild(span); + while (match !== null) { + let resource: uri | null = isAbsolute(match[1]) ? uri.file(match[1]) : null; + + if (!resource) { + match = pattern.exec(line); + continue; + } + + let textBeforeLink = line.substring(lastMatchIndex, match.index); + if (textBeforeLink) { + // textBeforeLink may have matches for other patterns, so we run handleLinks on it before adding it. + lineContainer.appendChild(this.handleLinks(textBeforeLink)); + } + + const link = document.createElement('a'); + link.textContent = line.substr(match.index, match[0].length); + link.title = isMacintosh ? nls.localize('fileLinkMac', "Click to follow (Cmd + click opens to the side)") : nls.localize('fileLink', "Click to follow (Ctrl + click opens to the side)"); + lineContainer.appendChild(link); + const lineNumber = Number(match[3]); + const columnNumber = match[4] ? Number(match[4]) : undefined; + link.onclick = (e) => this.onLinkClick(new StandardMouseEvent(e), resource!, lineNumber, columnNumber); + + lastMatchIndex = pattern.lastIndex; + const currentMatch = match; + match = pattern.exec(line); + + // Append last string part if no more link matches + if (!match) { + let textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); + if (textAfterLink) { + // textAfterLink may have matches for other patterns, so we run handleLinks on it before adding it. + lineContainer.appendChild(this.handleLinks(textAfterLink)); + } } } } - } - // append the text that went over the MAX_LENGTH limit - if (unalteredPostfix) { - if (linkContainer) { + if (!lineContainer.hasChildNodes()) { + // We didn't find a match for any of the patterns. Add the unmodified line. let span = document.createElement('span'); - span.textContent = unalteredPostfix; - linkContainer.appendChild(span); - } else { - text += unalteredPostfix; + span.textContent = line; + lineContainer.appendChild(span); } + + container.appendChild(lineContainer); } - return linkContainer || text; + return container; } private onLinkClick(event: IMouseEvent, resource: uri, line: number, column: number = 0): void { From fd638edefdac50bee9f52ef879e60524ae866086 Mon Sep 17 00:00:00 2001 From: Mathias Rasmussen Date: Sat, 5 Jan 2019 02:18:35 +0100 Subject: [PATCH 0066/1066] HTML Snippet: Consistent void tags --- extensions/html/snippets/html.snippets.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/html/snippets/html.snippets.json b/extensions/html/snippets/html.snippets.json index 7a858455a69..67c6e1bca7f 100644 --- a/extensions/html/snippets/html.snippets.json +++ b/extensions/html/snippets/html.snippets.json @@ -5,11 +5,11 @@ "", "", "", - "\t", + "\t", "\t", "\t${1:Page Title}", "\t", - "\t", + "\t", "\t", "", "", From 5d9e6414d4428bf320f3dc8468ede6c91e3d5bc6 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 5 Jan 2019 10:50:44 -0700 Subject: [PATCH 0067/1066] Properly handle multiple link detection patterns --- src/vs/workbench/parts/debug/browser/linkDetector.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index 881f4c7672b..33d12785e39 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -19,7 +19,8 @@ export class LinkDetector { // group 3: line number, matched by (:(\d+)) // group 4: column number, matched by ((?::(\d+))?) // eg: at Context. (c:\Users\someone\Desktop\mocha-runner\test\test.js:26:11) - /(?![\(])(?:file:\/\/)?((?:([a-zA-Z]+:)|[^\(\)<>\'\"\[\]:\s]+)(?:[\\/][^\(\)<>\'\"\[\]:]*)?\.[a-zA-Z]+[0-9]*):(\d+)(?::(\d+))?/g + /(?![\(])(?:file:\/\/)?((?:([a-zA-Z]+:)|[^\(\)<>\'\"\[\]:\s]+)(?:[\\/][^\(\)<>\'\"\[\]:]*)?\.[a-zA-Z]+[0-9]*):(\d+)(?::(\d+))?/g, + /module/ig, ]; constructor( @@ -78,7 +79,7 @@ export class LinkDetector { continue; } - let textBeforeLink = line.substring(lastMatchIndex, match.index); + const textBeforeLink = line.substring(lastMatchIndex, match.index); if (textBeforeLink) { // textBeforeLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textBeforeLink)); @@ -98,13 +99,18 @@ export class LinkDetector { // Append last string part if no more link matches if (!match) { - let textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); + const textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); if (textAfterLink) { // textAfterLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textAfterLink)); } } } + + // If we found a match, don't check any more patterns + if (lineContainer.hasChildNodes()) { + break; + } } if (!lineContainer.hasChildNodes()) { From a4bfe8698656586d25ebfa65cbc5e639d90831e3 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 5 Jan 2019 10:52:43 -0700 Subject: [PATCH 0068/1066] Revert "Properly handle multiple link detection patterns" This reverts commit 5d9e6414d4428bf320f3dc8468ede6c91e3d5bc6. --- src/vs/workbench/parts/debug/browser/linkDetector.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index 33d12785e39..881f4c7672b 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -19,8 +19,7 @@ export class LinkDetector { // group 3: line number, matched by (:(\d+)) // group 4: column number, matched by ((?::(\d+))?) // eg: at Context. (c:\Users\someone\Desktop\mocha-runner\test\test.js:26:11) - /(?![\(])(?:file:\/\/)?((?:([a-zA-Z]+:)|[^\(\)<>\'\"\[\]:\s]+)(?:[\\/][^\(\)<>\'\"\[\]:]*)?\.[a-zA-Z]+[0-9]*):(\d+)(?::(\d+))?/g, - /module/ig, + /(?![\(])(?:file:\/\/)?((?:([a-zA-Z]+:)|[^\(\)<>\'\"\[\]:\s]+)(?:[\\/][^\(\)<>\'\"\[\]:]*)?\.[a-zA-Z]+[0-9]*):(\d+)(?::(\d+))?/g ]; constructor( @@ -79,7 +78,7 @@ export class LinkDetector { continue; } - const textBeforeLink = line.substring(lastMatchIndex, match.index); + let textBeforeLink = line.substring(lastMatchIndex, match.index); if (textBeforeLink) { // textBeforeLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textBeforeLink)); @@ -99,18 +98,13 @@ export class LinkDetector { // Append last string part if no more link matches if (!match) { - const textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); + let textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); if (textAfterLink) { // textAfterLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textAfterLink)); } } } - - // If we found a match, don't check any more patterns - if (lineContainer.hasChildNodes()) { - break; - } } if (!lineContainer.hasChildNodes()) { From f769b707a73eec1a593ad95624d71abd6e099668 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 5 Jan 2019 10:56:17 -0700 Subject: [PATCH 0069/1066] Properly handle multiple link detection patterns --- src/vs/workbench/parts/debug/browser/linkDetector.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index 881f4c7672b..4bfd2b72671 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -78,7 +78,7 @@ export class LinkDetector { continue; } - let textBeforeLink = line.substring(lastMatchIndex, match.index); + const textBeforeLink = line.substring(lastMatchIndex, match.index); if (textBeforeLink) { // textBeforeLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textBeforeLink)); @@ -98,13 +98,18 @@ export class LinkDetector { // Append last string part if no more link matches if (!match) { - let textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); + const textAfterLink = line.substr(currentMatch.index + currentMatch[0].length); if (textAfterLink) { // textAfterLink may have matches for other patterns, so we run handleLinks on it before adding it. lineContainer.appendChild(this.handleLinks(textAfterLink)); } } } + + // If we found any matches for this pattern, don't check any more patterns + if (lineContainer.hasChildNodes()) { + break; + } } if (!lineContainer.hasChildNodes()) { From e0ca71671d2df9d7aa17538aae698a1393adba21 Mon Sep 17 00:00:00 2001 From: JimiC Date: Sun, 6 Jan 2019 19:01:59 +0200 Subject: [PATCH 0070/1066] Add ability to reload themes without restarting the editor --- .../themes/electron-browser/colorThemeData.ts | 22 +++-- .../electron-browser/fileIconThemeData.ts | 32 ++++--- .../electron-browser/workbenchThemeService.ts | 85 +++++++++++++++---- 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 59ee56d0edd..823f339d164 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -135,15 +135,21 @@ export class ColorThemeData implements IColorTheme { } public ensureLoaded(fileService: IFileService): Promise { - if (!this.isLoaded) { - if (this.location) { - return _loadColorTheme(fileService, this.location, this.themeTokenColors, this.colorMap).then(_ => { - this.isLoaded = true; - this.sanitizeTokenColors(); - }); - } + return !this.isLoaded ? this.load(fileService) : Promise.resolve(undefined); + } + + public reload(fileService: IFileService): Promise { + return this.load(fileService); + } + + private load(fileService: IFileService): Promise { + if (!this.location) { + return Promise.resolve(undefined); } - return Promise.resolve(undefined); + return _loadColorTheme(fileService, this.location, this.themeTokenColors, this.colorMap).then(_ => { + this.isLoaded = true; + this.sanitizeTokenColors(); + }); } /** diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index d1f0a073413..3815c063373 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -29,20 +29,26 @@ export class FileIconThemeData implements IFileIconTheme { private constructor() { } public ensureLoaded(fileService: IFileService): Promise { - if (!this.isLoaded) { - if (this.location) { - return _loadIconThemeDocument(fileService, this.location).then(iconThemeDocument => { - let result = _processIconThemeDocument(this.id, this.location!, iconThemeDocument); - this.styleSheetContent = result.content; - this.hasFileIcons = result.hasFileIcons; - this.hasFolderIcons = result.hasFolderIcons; - this.hidesExplorerArrows = result.hidesExplorerArrows; - this.isLoaded = true; - return this.styleSheetContent; - }); - } + return !this.isLoaded ? this.load(fileService) : Promise.resolve(this.styleSheetContent); + } + + public reload(fileService: IFileService): Promise { + return this.load(fileService); + } + + private load(fileService: IFileService): Promise { + if (!this.location) { + return Promise.resolve(this.styleSheetContent); } - return Promise.resolve(this.styleSheetContent); + return _loadIconThemeDocument(fileService, this.location).then(iconThemeDocument => { + const result = _processIconThemeDocument(this.id, this.location!, iconThemeDocument); + this.styleSheetContent = result.content; + this.hasFileIcons = result.hasFileIcons; + this.hasFolderIcons = result.hasFolderIcons; + this.hidesExplorerArrows = result.hidesExplorerArrows; + this.isLoaded = true; + return this.styleSheetContent; + }); } static fromExtensionTheme(iconTheme: IThemeExtensionPoint, iconThemeLocation: URI, extensionData: ExtensionData): FileIconThemeData { diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 90c579d0d73..71c24e5c3d5 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -26,7 +26,7 @@ import { FileIconThemeData } from 'vs/workbench/services/themes/electron-browser import { IWindowService } from 'vs/platform/windows/common/windows'; import { removeClasses, addClasses } from 'vs/base/browser/dom'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; // implementation @@ -72,10 +72,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; + private onColorThemeFileChangeListener: IDisposable; private iconThemeStore: FileIconThemeStore; private currentIconTheme: IFileIconTheme; private readonly onFileIconThemeChange: Emitter; + private onIconThemeFileChangeListener: IDisposable; private themingParticipantChangeListener: IDisposable; private _configurationWriter: ConfigurationWriter; @@ -279,27 +281,54 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeColorThemeConfiguration(settingsTarget); } + const onApply = (themeData: ColorThemeData) => { + // determine which color theme file to monitor for changes + if (this.fileService) { + const _currentColorThemeData = this.currentColorTheme as ColorThemeData; + if (!this.onColorThemeFileChangeListener) { + this.onColorThemeFileChangeListener = this.fileService.onFileChanges(e => { + return e.contains(themeData.location, FileChangeType.UPDATED) + && themeData.reload(this.fileService) + .then(_ => { + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(themeData); + }); + }); + } + // stop monitoring previous color theme file for changes + if (_currentColorThemeData.location) { + this.fileService.unwatchFileChanges(_currentColorThemeData.location); + } + // start monitoring new color theme file for changes + if (themeData.location) { + this.fileService.watchFileChanges(themeData.location); + } + } + }; + themeId = validateThemeId(themeId); // migrate theme ids return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { - if (themeData) { - return themeData.ensureLoaded(this.fileService).then(_ => { - if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { - // the loaded theme is identical to the perisisted theme. Don't need to send an event. - this.currentColorTheme = themeData; - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - return Promise.resolve(themeData); - } + if (!themeData) { + return null; + } + return themeData.ensureLoaded(this.fileService).then(_ => { + onApply(themeData); + if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { + // the loaded theme is identical to the perisisted theme. Don't need to send an event. + this.currentColorTheme = themeData; themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); - this.updateDynamicCSSRules(themeData); - return this.applyTheme(themeData, settingsTarget); - }, error => { - return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location.toString(), error.message))); - }); - } - return null; + return Promise.resolve(themeData); + } + themeData.setCustomColors(this.colorCustomizations); + themeData.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(themeData); + return this.applyTheme(themeData, settingsTarget); + }, error => { + return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location.toString(), error.message))); + }); }); } @@ -408,6 +437,28 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeFileIconConfiguration(settingsTarget); } let onApply = (newIconTheme: FileIconThemeData) => { + // determine which icon theme file to monitor for changes + if (this.currentIconTheme.id !== newIconTheme.id || !this.currentIconTheme.isLoaded) { + if (this.fileService) { + if (!this.onIconThemeFileChangeListener) { + this.onIconThemeFileChangeListener = this.fileService.onFileChanges(e => { + return e.contains(newIconTheme.location, FileChangeType.UPDATED) + && newIconTheme.reload(this.fileService) + .then(_ => _applyIconTheme(newIconTheme, () => Promise.resolve(this.currentIconTheme))); + }); + } + const _currentIconThemeData = this.currentIconTheme as FileIconThemeData; + // stop monitoring previous icon theme file for changes + if (_currentIconThemeData.location) { + this.fileService.unwatchFileChanges(_currentIconThemeData.location); + } + // start monitoring new icon theme file for changes + if (newIconTheme.location) { + this.fileService.watchFileChanges(newIconTheme.location); + } + } + } + this.doSetFileIconTheme(newIconTheme); // remember theme data for a quick restore From a7e590a4a667890f8a1bff5ce22d253a8d40a522 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 7 Jan 2019 15:18:51 +0100 Subject: [PATCH 0071/1066] fix file tests on windows --- src/vs/workbench/parts/files/electron-browser/fileActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 08b69e5d866..097b9ba355b 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -1027,7 +1027,7 @@ export function validateFileName(item: ExplorerItem, name: string): string { // Max length restriction (on Windows) if (isWindows) { - const fullPathLength = name.length + parent.resource.fsPath.length + 1 /* path segment */; + const fullPathLength = item.resource.fsPath.length + 1 /* path segment */; if (fullPathLength > 255) { return nls.localize('filePathTooLongError', "The name **{0}** results in a path that is too long. Please choose a shorter name.", trimLongName(name)); } From e23cb0078435b8f67df844a6f562c2f5e4cb292c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 7 Jan 2019 16:08:40 +0100 Subject: [PATCH 0072/1066] refactor --- .../electron-browser/fileIconThemeData.ts | 13 +++ .../electron-browser/workbenchThemeService.ts | 105 +++++++----------- 2 files changed, 55 insertions(+), 63 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 3815c063373..3fb0bc93d5e 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -81,6 +81,19 @@ export class FileIconThemeData implements IFileIconTheme { return themeData; } + static createUnloadedTheme(id: string): FileIconThemeData { + let themeData = new FileIconThemeData(); + themeData.id = ''; + themeData.label = ''; + themeData.settingsId = null; + themeData.isLoaded = false; + themeData.hasFileIcons = false; + themeData.hasFolderIcons = false; + themeData.hidesExplorerArrows = false; + themeData.extensionData = null; + return themeData; + } + static fromStorageData(input: string): FileIconThemeData | null { try { let data = JSON.parse(input); diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 71c24e5c3d5..aa2ad1e5ca0 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -27,6 +27,8 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { removeClasses, addClasses } from 'vs/base/browser/dom'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import * as resources from 'vs/base/common/resources'; // implementation @@ -72,12 +74,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; - private onColorThemeFileChangeListener: IDisposable; + private watchedColorThemeLocation: URI; private iconThemeStore: FileIconThemeStore; - private currentIconTheme: IFileIconTheme; + private currentIconTheme: FileIconThemeData; private readonly onFileIconThemeChange: Emitter; - private onIconThemeFileChangeListener: IDisposable; + private watchedIconThemeLocation: URI; private themingParticipantChangeListener: IDisposable; private _configurationWriter: ConfigurationWriter; @@ -107,16 +109,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.iconThemeStore = new FileIconThemeStore(extensionService); this.onColorThemeChange = new Emitter(); - this.currentIconTheme = { - id: '', - label: '', - settingsId: null, - isLoaded: false, - hasFileIcons: false, - hasFolderIcons: false, - hidesExplorerArrows: false, - extensionData: null - }; + this.currentIconTheme = FileIconThemeData.createUnloadedTheme(''); // In order to avoid paint flashing for tokens, because // themes are loaded asynchronously, we need to initialize @@ -183,6 +176,20 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { acquireFileService(fileService: IFileService): void { this.fileService = fileService; + + this.fileService.onFileChanges(async e => { + if (this.watchedColorThemeLocation && this.currentColorTheme && e.contains(this.watchedColorThemeLocation, FileChangeType.UPDATED)) { + await this.currentColorTheme.reload(this.fileService); + this.currentColorTheme.setCustomColors(this.colorCustomizations); + this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); + this.updateDynamicCSSRules(this.currentColorTheme); + this.applyTheme(this.currentColorTheme, null, false); + } + if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { + await this.currentIconTheme.reload(this.fileService); + _applyIconTheme(this.currentIconTheme, () => Promise.resolve(this.currentIconTheme)); + } + }); } public get onDidColorThemeChange(): Event { @@ -281,32 +288,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeColorThemeConfiguration(settingsTarget); } - const onApply = (themeData: ColorThemeData) => { - // determine which color theme file to monitor for changes - if (this.fileService) { - const _currentColorThemeData = this.currentColorTheme as ColorThemeData; - if (!this.onColorThemeFileChangeListener) { - this.onColorThemeFileChangeListener = this.fileService.onFileChanges(e => { - return e.contains(themeData.location, FileChangeType.UPDATED) - && themeData.reload(this.fileService) - .then(_ => { - themeData.setCustomColors(this.colorCustomizations); - themeData.setCustomTokenColors(this.tokenColorCustomizations); - this.updateDynamicCSSRules(themeData); - }); - }); - } - // stop monitoring previous color theme file for changes - if (_currentColorThemeData.location) { - this.fileService.unwatchFileChanges(_currentColorThemeData.location); - } - // start monitoring new color theme file for changes - if (themeData.location) { - this.fileService.watchFileChanges(themeData.location); - } - } - }; - themeId = validateThemeId(themeId); // migrate theme ids return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { @@ -314,7 +295,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return null; } return themeData.ensureLoaded(this.fileService).then(_ => { - onApply(themeData); if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { // the loaded theme is identical to the perisisted theme. Don't need to send an event. this.currentColorTheme = themeData; @@ -369,7 +349,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } this.currentColorTheme = newTheme; if (!this.themingParticipantChangeListener) { - this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(p => this.updateDynamicCSSRules(this.currentColorTheme)); + this.themingParticipantChangeListener = themingRegistry.onThemingParticipantAdded(_ => this.updateDynamicCSSRules(this.currentColorTheme)); + } + + if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { + if (this.watchedColorThemeLocation) { + this.fileService.unwatchFileChanges(this.watchedColorThemeLocation); + } + this.watchedColorThemeLocation = newTheme.location; + if (this.watchedColorThemeLocation) { + this.fileService.watchFileChanges(this.watchedColorThemeLocation); + } } this.sendTelemetry(newTheme.id, newTheme.extensionData, 'color'); @@ -437,28 +427,6 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeFileIconConfiguration(settingsTarget); } let onApply = (newIconTheme: FileIconThemeData) => { - // determine which icon theme file to monitor for changes - if (this.currentIconTheme.id !== newIconTheme.id || !this.currentIconTheme.isLoaded) { - if (this.fileService) { - if (!this.onIconThemeFileChangeListener) { - this.onIconThemeFileChangeListener = this.fileService.onFileChanges(e => { - return e.contains(newIconTheme.location, FileChangeType.UPDATED) - && newIconTheme.reload(this.fileService) - .then(_ => _applyIconTheme(newIconTheme, () => Promise.resolve(this.currentIconTheme))); - }); - } - const _currentIconThemeData = this.currentIconTheme as FileIconThemeData; - // stop monitoring previous icon theme file for changes - if (_currentIconThemeData.location) { - this.fileService.unwatchFileChanges(_currentIconThemeData.location); - } - // start monitoring new icon theme file for changes - if (newIconTheme.location) { - this.fileService.watchFileChanges(newIconTheme.location); - } - } - } - this.doSetFileIconTheme(newIconTheme); // remember theme data for a quick restore @@ -487,6 +455,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { removeClasses(this.container, fileIconsEnabledClass); } } + + if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { + if (this.watchedIconThemeLocation) { + this.fileService.unwatchFileChanges(this.watchedIconThemeLocation); + } + this.watchedIconThemeLocation = iconThemeData.location; + if (this.watchedIconThemeLocation) { + this.fileService.watchFileChanges(this.watchedIconThemeLocation); + } + } + if (iconThemeData.id) { this.sendTelemetry(iconThemeData.id, iconThemeData.extensionData, 'fileIcon'); } From 04c3dde6e49613d716dd16ef253c6351d8b5077d Mon Sep 17 00:00:00 2001 From: Ilya Biryukov Date: Mon, 7 Jan 2019 11:31:28 -0800 Subject: [PATCH 0073/1066] 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 2ab9c979fa5ea96ad38017074a990c89569a67a8 Mon Sep 17 00:00:00 2001 From: "Jimi (Dimitris) Charalampidis" Date: Tue, 8 Jan 2019 11:35:22 +0200 Subject: [PATCH 0074/1066] Fix null checks --- .../services/themes/electron-browser/fileIconThemeData.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 3fb0bc93d5e..56f44509dcb 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -85,12 +85,12 @@ export class FileIconThemeData implements IFileIconTheme { let themeData = new FileIconThemeData(); themeData.id = ''; themeData.label = ''; - themeData.settingsId = null; + themeData.settingsId = undefined; themeData.isLoaded = false; themeData.hasFileIcons = false; themeData.hasFolderIcons = false; themeData.hidesExplorerArrows = false; - themeData.extensionData = null; + themeData.extensionData = undefined; return themeData; } From a6b761b033e4b936865b8a35b2a8340fd9e908bd Mon Sep 17 00:00:00 2001 From: pkoushik Date: Tue, 8 Jan 2019 15:22:29 +0530 Subject: [PATCH 0075/1066] fix-65575 Updated tokenize method - Returns cached token if exists or newly created token and cache it --- .../src/markdownEngine.ts | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 651770b282a..e66c64423df 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -97,20 +97,21 @@ export class MarkdownEngine { return { text, offset }; } - private tokenize(document: SkinnyTextDocument, engine?: MarkdownIt): Token[] { + private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; const { text, offset } = this.stripFrontmatter(document.getText()); - if (this.cachedTokens.has(document.uri)) { - return this.cachedTokens.get(document.uri)!; + const uri = document.uri; + if (this.cachedTokens.has(uri)) { + return this.cachedTokens.get(uri)!; } - const tokens = engine!.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { + const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { if (token.map) { token.map[0] += offset; token.map[1] += offset; } return token; }); - this.cachedTokens.set(document.uri, tokens); + this.cachedTokens.set(uri, tokens); return tokens; } @@ -127,25 +128,14 @@ export class MarkdownEngine { this._slugCount = new Map(); const engine = await this.getEngine(document.uri); - let tokens: Token[]; - tokens = this.tokenize(document); - if (!tokens) { - tokens = this.tokenize(document, engine); - } - return engine.renderer.render(tokens, this.md, {}); + return engine.renderer.render(this.tokenize(document, engine), this.md, {}); } public async parse(document: SkinnyTextDocument): Promise { this.currentDocument = document.uri; this._slugCount = new Map(); - const engine = await this.getEngine(document.uri); - let tokens: Token[]; - tokens = this.tokenize(document); - if (!tokens) { - tokens = this.tokenize(document, engine); - } - return tokens; + return this.tokenize(document, engine); } private addLineNumberRenderer(md: any, ruleName: string): void { From 2dc54a11cb0e19601a35643fe21968bf1a6e189a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 8 Jan 2019 11:52:20 +0100 Subject: [PATCH 0076/1066] list: remove focusOnMouseDown --- src/vs/base/browser/ui/list/listWidget.ts | 26 ++++++++----------- .../browser/ui/selectBox/selectBoxCustom.ts | 1 - .../editor/contrib/suggest/media/suggest.css | 11 +++++++- .../editor/contrib/suggest/suggestWidget.ts | 19 +++++++++----- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index b7ccbc9f294..43da65ef1fc 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -488,6 +488,7 @@ class MouseController implements IDisposable { this.openController = options.openController || DefaultOpenController; view.onMouseDown(this.onMouseDown, this, this.disposables); + view.onContextMenu(this.onContextMenu, this, this.disposables); view.onMouseClick(this.onPointer, this, this.disposables); view.onMouseDblClick(this.onDoubleClick, this, this.disposables); view.onTouchStart(this.onMouseDown, this, this.disposables); @@ -516,13 +517,17 @@ class MouseController implements IDisposable { } private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { - if (this.options.focusOnMouseDown === false) { - e.browserEvent.preventDefault(); - e.browserEvent.stopPropagation(); - } else if (document.activeElement !== e.browserEvent.target) { + if (document.activeElement !== e.browserEvent.target) { this.view.domNode.focus(); } + } + private onContextMenu(e: IListMouseEvent | IListTouchEvent): void { + const focus = typeof e.index === 'undefined' ? [] : [e.index]; + this.list.setFocus(focus, e.browserEvent); + } + + private onPointer(e: IListMouseEvent): void { let reference = this.list.getFocus()[0]; const selection = this.list.getSelection(); reference = reference === undefined ? selection[0] : reference; @@ -539,14 +544,12 @@ class MouseController implements IDisposable { return this.changeSelection(e, reference); } - if (selection.every(s => s !== focus)) { - this.list.setFocus([focus], e.browserEvent); - } - if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { return this.changeSelection(e, reference); } + this.list.setFocus([focus], e.browserEvent); + if (this.options.selectOnMouseDown && !isMouseRightClick(e.browserEvent)) { this.list.setSelection([focus], e.browserEvent); @@ -554,12 +557,6 @@ class MouseController implements IDisposable { this.list.open([focus], e.browserEvent); } } - } - - private onPointer(e: IListMouseEvent): void { - if (this.multipleSelectionSupport && this.isSelectionChangeEvent(e)) { - return; - } if (!this.options.selectOnMouseDown) { const focus = this.list.getFocus(); @@ -728,7 +725,6 @@ export interface IListOptions extends IListViewOptions, IListStyles { ariaLabel?: string; mouseSupport?: boolean; selectOnMouseDown?: boolean; - focusOnMouseDown?: boolean; keyboardSupport?: boolean; verticalScrollMode?: ScrollbarVisibility; multipleSelectionSupport?: boolean; diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 189f657a716..77ca7e4f06e 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -731,7 +731,6 @@ export class SelectBoxList implements ISelectBoxDelegate, IListVirtualDelegate .contents { diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index da8d189bc5b..263748eeeca 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -11,7 +11,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { addClass, append, $, hide, removeClass, show, toggleClass, getDomNodePagePosition, hasClass } from 'vs/base/browser/dom'; -import { IListVirtualDelegate, IListEvent, IListRenderer } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IListEvent, IListRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -470,9 +470,8 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate false } + openController: { shouldOpen: () => false }, + mouseSupport: false }); this.toDispose = [ @@ -482,6 +481,7 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate this.onThemeChange(t)), editor.onDidLayoutChange(() => this.onEditorLayoutChange()), + this.list.onMouseDown(e => this.onListMouseDown(e)), this.list.onSelectionChange(e => this.onListSelection(e)), this.list.onFocusChange(e => this.onListFocus(e)), this.editor.onDidChangeCursorSelection(() => this.onCursorSelectionChanged()) @@ -510,18 +510,25 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate): void { + this.select(e.element, e.index); + } + private onListSelection(e: IListEvent): void { if (!e.elements.length) { return; } + this.select(e.elements[0], e.indexes[0]); + } + + private select(item: CompletionItem, index: number): void { const completionModel = this.completionModel; + if (!completionModel) { return; } - const item = e.elements[0]; - const index = e.indexes[0]; item.resolve(CancellationToken.None).then(() => { this.onDidSelectEmitter.fire({ item, index, model: completionModel }); alert(nls.localize('suggestionAriaAccepted', "{0}, accepted", item.completion.label)); From 09dc00b92181a90c1dd4614b794ca0a7fff859e3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 8 Jan 2019 15:58:13 +0100 Subject: [PATCH 0077/1066] fix strict null in suggest widget --- src/vs/editor/contrib/suggest/suggestWidget.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 263748eeeca..347de4b6499 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -511,6 +511,10 @@ export class SuggestWidget implements IContentWidget, IListVirtualDelegate): void { + if (typeof e.element === 'undefined' || typeof e.index === 'undefined') { + return; + } + this.select(e.element, e.index); } From 669b8c9cd476df5aa1a989ed05427d19a59bc713 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 8 Jan 2019 15:58:49 +0100 Subject: [PATCH 0078/1066] list: remove selectOnMouseDown --- src/vs/base/browser/ui/list/listWidget.ts | 14 ++------------ src/vs/platform/list/browser/listService.ts | 5 ----- .../electron-browser/views/openEditorsView.ts | 3 +-- 3 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 43da65ef1fc..29b226215c7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -477,7 +477,7 @@ class MouseController implements IDisposable { constructor( private list: List, private view: ListView, - private options: IListOptions = {} + options: IListOptions = {} ) { this.multipleSelectionSupport = !(options.multipleSelectionSupport === false); @@ -550,22 +550,13 @@ class MouseController implements IDisposable { this.list.setFocus([focus], e.browserEvent); - if (this.options.selectOnMouseDown && !isMouseRightClick(e.browserEvent)) { + if (!isMouseRightClick(e.browserEvent)) { this.list.setSelection([focus], e.browserEvent); if (this.openController.shouldOpen(e.browserEvent)) { this.list.open([focus], e.browserEvent); } } - - if (!this.options.selectOnMouseDown) { - const focus = this.list.getFocus(); - this.list.setSelection(focus, e.browserEvent); - - if (this.openController.shouldOpen(e.browserEvent)) { - this.list.open(focus, e.browserEvent); - } - } } private onDoubleClick(e: IListMouseEvent): void { @@ -724,7 +715,6 @@ export interface IListOptions extends IListViewOptions, IListStyles { keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; ariaLabel?: string; mouseSupport?: boolean; - selectOnMouseDown?: boolean; keyboardSupport?: boolean; verticalScrollMode?: ScrollbarVisibility; multipleSelectionSupport?: boolean; diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index efd33daddf6..04f706b9377 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -234,7 +234,6 @@ export class WorkbenchList extends List { super(container, delegate, renderers, { keyboardSupport: false, - selectOnMouseDown: true, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService) @@ -310,7 +309,6 @@ export class WorkbenchPagedList extends PagedList { super(container, delegate, renderers, { keyboardSupport: false, - selectOnMouseDown: true, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService) @@ -903,7 +901,6 @@ export class WorkbenchObjectTree, TFilterData = void> ) { super(container, delegate, renderers, { keyboardSupport: false, - selectOnMouseDown: true, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService) @@ -980,7 +977,6 @@ export class WorkbenchDataTree extends DataTree extends Async ) { super(container, delegate, renderers, dataSource, { keyboardSupport: false, - selectOnMouseDown: true, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService) 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 c9372a0affd..a74ef62d051 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -222,8 +222,7 @@ export class OpenEditorsView extends ViewletPanel { new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService), new OpenEditorRenderer(this.listLabels, getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) ], { - identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() }, - selectOnMouseDown: false /* disabled to better support DND */ + identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() } }) as WorkbenchList; this.disposables.push(this.list); this.disposables.push(this.listLabels); From fd3188fbb60e63871b9ba669ee5abce91bc716c3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 8 Jan 2019 16:41:39 +0100 Subject: [PATCH 0079/1066] wip: basic list dnd with animation --- src/vs/base/browser/dom.ts | 17 +- src/vs/base/browser/ui/list/list.css | 9 ++ src/vs/base/browser/ui/list/list.ts | 40 +++++ src/vs/base/browser/ui/list/listView.ts | 116 ++++++++------ src/vs/base/browser/ui/list/listWidget.ts | 145 +++++++++++++++++- src/vs/base/browser/ui/tree/abstractTree.ts | 17 ++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 17 ++ .../parts/scm/electron-browser/scmViewlet.ts | 9 +- 8 files changed, 311 insertions(+), 59 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d765620746f..868b77ade0e 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -11,7 +11,7 @@ import { TimeoutTimer } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { coalesce } from 'vs/base/common/arrays'; @@ -1157,3 +1157,18 @@ export function windowOpenNoOpener(url: string): void { } } } + +export function animate(fn: () => void): IDisposable { + const step = () => { + fn(); + stepDisposable = scheduleAtNextAnimationFrame(step); + }; + + let stepDisposable = scheduleAtNextAnimationFrame(step); + return toDisposable(() => stepDisposable.dispose()); +} + +export function timeout(fn: () => void, millis: number): IDisposable { + const timer = setTimeout(fn, millis); + return toDisposable(() => clearTimeout(timer)); +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index a651191df53..55038eb39b6 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -52,4 +52,13 @@ /* Focus */ .monaco-list.element-focused, .monaco-list.selection-single, .monaco-list.selection-multiple { outline: 0 !important; +} + +/* Dnd */ +.monaco-list-drag-image { + display: inline-block; + padding: 1px 7px; + border-radius: 10px; + font-size: 12px; + position: absolute; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 1db53632a68..1d502b622c7 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -7,6 +7,7 @@ import { GestureEvent } from 'vs/base/browser/touch'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; export interface IListVirtualDelegate { getHeight(element: T): number; @@ -62,6 +63,45 @@ export interface IKeyboardNavigationLabelProvider { mightProducePrintableCharacter?(event: IKeyboardEvent): boolean; } +export const enum DragOverEffect { + Copy, + Move +} + +// export const enum DragOverBubble { +// Down, +// Up +// } + +export interface IDragOverReaction { + accept: boolean; + effect?: DragOverEffect; + // bubble?: DragOverBubble; + // autoExpand?: boolean; +} + +export const DragOverReactions = { + reject(): IDragOverReaction { return { accept: false }; }, + accept(): IDragOverReaction { return { accept: true }; }, + // acceptBubbleUp(): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Up }; }, + // acceptBubbleDown(autoExpand = false): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Down, autoExpand }; }, + // acceptCopyBubbleUp(): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Up, effect: DragOverEffect.Copy }; }, + // acceptCopyBubbleDown(autoExpand = false): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Down, effect: DragOverEffect.Copy, autoExpand }; } +}; + +export interface IDragAndDropData { + update(event: DragMouseEvent): void; + getData(): any; +} + +export interface IDragAndDrop { + getDragURI(element: T): string | null; + getDragLabel?(elements: T[]): string; + onDragStart(data: IDragAndDropData, originalEvent: DragMouseEvent): void; + onDragOver(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): boolean | IDragOverReaction; + drop(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): void; +} + /** * Use this renderer when you want to re-render elements on account of * an event firing. diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index e62db1ff2eb..6062e33ebdc 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getOrDefault } from 'vs/base/common/objects'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; @@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IDragAndDrop } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; @@ -41,9 +41,12 @@ interface IItem { size: number; hasDynamicHeight: boolean; renderWidth: number | undefined; + uri: string | undefined; + dragStartDisposable: IDisposable; } -export interface IListViewOptions { +export interface IListViewOptions { + readonly dnd?: IDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; @@ -55,7 +58,13 @@ const DefaultOptions = { useShadows: true, verticalScrollMode: ScrollbarVisibility.Auto, setRowLineHeight: true, - supportDynamicHeights: false + supportDynamicHeights: false, + dnd: { + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + } }; export class ListView implements ISpliceable, IDisposable { @@ -76,22 +85,26 @@ export class ListView implements ISpliceable, IDisposable { private _scrollHeight: number; private scrollableElementUpdateDisposable: IDisposable | null = null; private splicing = false; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragOverAnimationDisposable: IDisposable | undefined; + private dragOverAnimationStopDisposable: IDisposable = Disposable.None; + private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; + private readonly dnd: IDragAndDrop; private disposables: IDisposable[]; private _onDidChangeContentHeight = new Emitter(); readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); get contentHeight(): number { return this.rangeMap.size; } + private _onDragStart = new Emitter<{ element: T, uri: string, event: DragMouseEvent }>(); + readonly onDragStart = this._onDragStart.event; + constructor( container: HTMLElement, private virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], - options: IListViewOptions = DefaultOptions + options: IListViewOptions = DefaultOptions ) { this.items = []; this.itemId = 0; @@ -136,9 +149,12 @@ export class ListView implements ISpliceable, IDisposable { const onDragOver = Event.map(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); onDragOver(this.onDragOver, this, this.disposables); + const onDragEnd = Event.map(domEvent(document, 'dragend'), e => new DragMouseEvent(e)); + onDragEnd(this.onDragEnd, this, this.disposables); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); + this.dnd = getOrDefault, IDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); this.layout(); } @@ -178,7 +194,9 @@ export class ListView implements ISpliceable, IDisposable { size: this.virtualDelegate.getHeight(element), hasDynamicHeight: !!this.virtualDelegate.hasDynamicHeight && this.virtualDelegate.hasDynamicHeight(element), renderWidth: undefined, - row: null + row: null, + uri: undefined, + dragStartDisposable: Disposable.None })); let deleted: IItem[]; @@ -354,6 +372,15 @@ export class ListView implements ISpliceable, IDisposable { const renderer = this.renderers.get(item.templateId); renderer.renderElement(item.element, index, item.row.templateData); + + const uri = this.dnd.getDragURI(item.element); + item.dragStartDisposable.dispose(); + + if (uri) { + item.row.domNode!.draggable = true; + const onDragStart = Event.map(domEvent(item.row.domNode!, 'dragstart'), e => new DragMouseEvent(e)); + item.dragStartDisposable = onDragStart(event => this._onDragStart.fire({ element: item.element, uri, event }), this); + } } private updateItemInDOM(item: IItem, index: number): void { @@ -372,6 +399,8 @@ export class ListView implements ISpliceable, IDisposable { private removeItemFromDOM(index: number): void { const item = this.items[index]; + item.dragStartDisposable.dispose(); + const renderer = this.renderers.get(item.templateId); if (renderer.disposeElement) { @@ -464,55 +493,44 @@ export class ListView implements ISpliceable, IDisposable { this.scrollTop -= event.translationY; } + // DND + private onDragOver(event: DragMouseEvent): void { - this.setupDragAndDropScrollInterval(); - this.dragAndDropMouseY = event.posy; + if (!this.dragOverAnimationDisposable) { + const viewTop = DOM.getTopLeftOffset(this.domNode).top; + this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); + } + + this.dragOverAnimationStopDisposable.dispose(); + this.dragOverAnimationStopDisposable = DOM.timeout(() => { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; + }, 1000); + + this.dragOverMouseY = event.posy; } - private setupDragAndDropScrollInterval(): void { - const viewTop = DOM.getTopLeftOffset(this.domNode).top; + private animateDragAndDropScrollTop(viewTop: number): void { + if (this.dragOverMouseY === undefined) { + return; + } - if (!this.dragAndDropScrollInterval) { - this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { - return; - } + const diff = this.dragOverMouseY - viewTop; + const upperLimit = this.renderHeight - 35; - let diff = this.dragAndDropMouseY - viewTop; - let scrollDiff = 0; - let upperLimit = this.renderHeight - 35; - - if (diff < 35) { - scrollDiff = Math.max(-14, 0.2 * (diff - 35)); - } else if (diff > upperLimit) { - scrollDiff = Math.min(14, 0.2 * (diff - upperLimit)); - } - - this.scrollTop += scrollDiff; - }, 10); - - this.cancelDragAndDropScrollTimeout(); - - this.dragAndDropScrollTimeout = window.setTimeout(() => { - this.cancelDragAndDropScrollInterval(); - this.dragAndDropScrollTimeout = -1; - }, 1000); + if (diff < 35) { + this.scrollTop += Math.max(-14, Math.floor(0.3 * (diff - 35))); + } else if (diff > upperLimit) { + this.scrollTop += Math.min(14, Math.floor(0.3 * (diff - upperLimit))); } } - private cancelDragAndDropScrollInterval(): void { - if (this.dragAndDropScrollInterval) { - window.clearInterval(this.dragAndDropScrollInterval); - this.dragAndDropScrollInterval = -1; - } + private onDragEnd(): void { + this.dragOverAnimationStopDisposable.dispose(); - this.cancelDragAndDropScrollTimeout(); - } - - private cancelDragAndDropScrollTimeout(): void { - if (this.dragAndDropScrollTimeout) { - window.clearTimeout(this.dragAndDropScrollTimeout); - this.dragAndDropScrollTimeout = -1; + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; } } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 29b226215c7..e83d5aa2164 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,15 +16,17 @@ 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 } from './list'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IDragAndDrop, IDragAndDropData } from './list'; import { ListView, IListViewOptions } from './listView'; import { Color } from 'vs/base/common/color'; -import { mixin } from 'vs/base/common/objects'; +import { mixin, getOrDefault } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; import { matchesPrefix } from 'vs/base/common/filters'; +import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; +import { DataTransfers } from 'vs/base/browser/dnd'; interface ITraitChangeEvent { indexes: number[]; @@ -658,11 +660,17 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusAndSelectionBackground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } + `); } if (styles.listFocusAndSelectionForeground) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } + `); } if (styles.listInactiveFocusBackground) { @@ -692,7 +700,10 @@ export class DefaultStyleController implements IStyleController { } if (styles.listFocusOutline) { - content.push(`.monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; }`); + content.push(` + .monaco-list-drag-image, + .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } + `); } if (styles.listInactiveFocusOutline) { @@ -703,6 +714,14 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); } + // TODO + // if (styles.listDropBackground) { + // content.push(` + // .monaco-tree${suffix} .monaco-tree-wrapper.drop-target, + // .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } + // `); + // } + const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { this.styleElement.innerHTML = newStyles; @@ -710,8 +729,9 @@ export class DefaultStyleController implements IStyleController { } } -export interface IListOptions extends IListViewOptions, IListStyles { +export interface IListOptions extends IListViewOptions, IListStyles { identityProvider?: IIdentityProvider; + dnd?: IDragAndDrop; keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; ariaLabel?: string; mouseSupport?: boolean; @@ -757,7 +777,13 @@ const defaultStyles: IListStyles = { const DefaultOptions: IListOptions = { keyboardSupport: true, mouseSupport: true, - multipleSelectionSupport: true + multipleSelectionSupport: true, + dnd: { + getDragURI() { return null; }, + onDragStart(): void { }, + onDragOver() { return false; }, + drop() { } + } }; // TODO@Joao: move these utils into a SortedArray class @@ -912,8 +938,73 @@ class AccessibiltyRenderer implements IListRenderer { } } +export class ElementsDragAndDropData implements IDragAndDropData { + + private elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + public update(event: DragMouseEvent): void { + // no-op + } + + public getData(): any { + return this.elements; + } +} + +export class ExternalElementsDragAndDropData implements IDragAndDropData { + + private elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + public update(event: DragMouseEvent): void { + // no-op + } + + public getData(): any { + return this.elements; + } +} + +export class DesktopDragAndDropData implements IDragAndDropData { + + private types: any[]; + private files: any[]; + + constructor() { + this.types = []; + this.files = []; + } + + public update(event: DragMouseEvent): void { + if (event.dataTransfer.types) { + this.types = [...event.dataTransfer.types]; + } + + if (event.dataTransfer.files) { + this.files = [...event.dataTransfer.files]; + this.files = this.files.filter(f => f.size || f.type); + } + } + + public getData(): any { + return { + types: this.types, + files: this.files + }; + } +} + export class List implements ISpliceable, IDisposable { + private static currentExternalDragAndDropData: IDragAndDropData | undefined; + private static InstanceCount = 0; private idPrefix = `list_id_${++List.InstanceCount}`; @@ -922,10 +1013,14 @@ export class List implements ISpliceable, IDisposable { private eventBufferer = new EventBufferer(); private view: ListView; private spliceable: ISpliceable; - protected disposables: IDisposable[]; private styleElement: HTMLStyleElement; private styleController: IStyleController; + private readonly dnd: IDragAndDrop; + private currentDragAndDropData: IDragAndDropData | undefined; + + protected disposables: IDisposable[]; + @memoize get onFocusChange(): Event> { return Event.map(this.eventBufferer.wrapEvent(this.focus.onChange), e => this.toListEvent(e)); } @@ -1009,6 +1104,7 @@ export class List implements ISpliceable, IDisposable { ) { this.focus = new FocusTrait(i => this.getElementDomId(i)); this.selection = new Trait('selected'); + this.dnd = getOrDefault, IDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); mixin(options, defaultStyles, false); @@ -1056,6 +1152,8 @@ export class List implements ISpliceable, IDisposable { this.disposables.push(new MouseController(this, this.view, options)); } + this.view.onDragStart(this.onDragStart, this, this.disposables); + this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); @@ -1342,6 +1440,37 @@ export class List implements ISpliceable, IDisposable { DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1); } + // DND + + private onDragStart({ element, uri, event }: { element: T, uri: string, event: DragMouseEvent }): void { + const selection = this.getSelectedElements(); + const elements = selection.indexOf(element) > -1 ? selection : [element]; + + event.dataTransfer.effectAllowed = 'copyMove'; + event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); + + if (event.dataTransfer.setDragImage) { + let label: string; + + if (this.dnd.getDragLabel) { + label = this.dnd.getDragLabel(elements); + } else { + label = String(elements.length); + } + + const dragImage = DOM.$('.monaco-list-drag-image'); + dragImage.textContent = label; + document.body.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + } + + this.currentDragAndDropData = new ElementsDragAndDropData(elements); + List.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements); + + this.dnd.onDragStart(this.currentDragAndDropData, event); + } + dispose(): void { this._onDidDispose.fire(); this.disposables = dispose(this.disposables); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 09cc5afa457..0ece88b2d95 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -22,6 +22,23 @@ function asListOptions(options?: IAbstractTreeOptions { + return options.dnd!.getDragLabel!(nodes.map(node => node.element)); + }), + onDragStart(data, originalEvent) { + return options.dnd!.onDragStart(data, originalEvent); + }, + onDragOver(data, targetNode, originalEvent) { + return options.dnd!.onDragOver(data, targetNode.element, originalEvent); + }, + drop(data, targetNode, originalEvent) { + return options.dnd!.drop(data, targetNode.element, originalEvent); + } + }, multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index b0970cb21bb..48bb09a8909 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -130,6 +130,23 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt return options.identityProvider!.getId(el.element as T); } }, + dnd: options.dnd && { + getDragURI(node) { + return options.dnd!.getDragURI(node.element as T); + }, + getDragLabel: options.dnd!.getDragLabel && ((nodes) => { + return options.dnd!.getDragLabel!(nodes.map(node => node.element as T)); + }), + onDragStart(data, originalEvent) { + return options.dnd!.onDragStart(data, originalEvent); + }, + onDragOver(data, targetNode, originalEvent) { + return options.dnd!.onDragOver(data, targetNode.element as T, originalEvent); + }, + drop(data, targetNode, originalEvent) { + return options.dnd!.drop(data, targetNode.element as T, originalEvent); + } + }, multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 19d30856bec..4d619e03f5d 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -880,7 +880,14 @@ export class RepositoryPanel extends ViewletPanel { this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { identityProvider: scmResourceIdentityProvider, - keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider + keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider, + dnd: { + getDragURI(element) { return 'file:///foo'; }, + // getDragLabel(elements) { return 'dragging'; }, + onDragStart(data, originalEvent) { }, + onDragOver(data, targetElement, originalEvent) { return true; }, + drop(data, targetElement, originalEvent) { } + } }) as WorkbenchList; Event.chain(this.list.onDidOpen) From e87692c6eac242950a17e41c641058aa8f094651 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 8 Jan 2019 17:46:07 +0100 Subject: [PATCH 0080/1066] list: more dnd --- src/vs/base/browser/ui/list/list.ts | 6 ++++++ src/vs/base/browser/ui/list/listView.ts | 26 +++++++++++++++++------ src/vs/base/browser/ui/list/listWidget.ts | 13 +++++++++++- 3 files changed, 38 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 1d502b622c7..09da2945df9 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -47,6 +47,12 @@ export interface IListGestureEvent { index: number | undefined; } +export interface IListDragEvent { + browserEvent: DragEvent; + element: T | undefined; + index: number | undefined; +} + export interface IListContextMenuEvent { browserEvent: UIEvent; element: T | undefined; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 6062e33ebdc..d7ec9514290 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IDragAndDrop } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IDragAndDrop, IListDragEvent } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; @@ -100,6 +100,9 @@ export class ListView implements ISpliceable, IDisposable { private _onDragStart = new Emitter<{ element: T, uri: string, event: DragMouseEvent }>(); readonly onDragStart = this._onDragStart.event; + readonly onDragOver: Event>; + readonly onDragLeave: Event; + constructor( container: HTMLElement, private virtualDelegate: IListVirtualDelegate, @@ -147,10 +150,14 @@ export class ListView implements ISpliceable, IDisposable { domEvent(this.scrollableElement.getDomNode(), 'scroll') (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); - const onDragOver = Event.map(domEvent(this.rowsContainer, 'dragover'), e => new DragMouseEvent(e)); - onDragOver(this.onDragOver, this, this.disposables); + const onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => new DragMouseEvent(e)); + onDragOver(this.onDidDragOver, this, this.disposables); + + this.onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e)); + this.onDragLeave = Event.signal(domEvent(this.domNode, 'dragleave')); + const onDragEnd = Event.map(domEvent(document, 'dragend'), e => new DragMouseEvent(e)); - onDragEnd(this.onDragEnd, this, this.disposables); + onDragEnd(this._onDragEnd, this, this.disposables); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); @@ -473,6 +480,13 @@ export class ListView implements ISpliceable, IDisposable { return { browserEvent, index, element }; } + private toDragEvent(browserEvent: DragEvent): IListDragEvent { + const index = this.getItemIndexFromEventTarget(browserEvent.target || null); + const item = typeof index === 'undefined' ? undefined : this.items[index]; + const element = item && item.element; + return { browserEvent, index, element }; + } + private onScroll(e: ScrollEvent): void { try { this.render(e.scrollTop, e.height); @@ -495,7 +509,7 @@ export class ListView implements ISpliceable, IDisposable { // DND - private onDragOver(event: DragMouseEvent): void { + private onDidDragOver(event: DragMouseEvent): void { if (!this.dragOverAnimationDisposable) { const viewTop = DOM.getTopLeftOffset(this.domNode).top; this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); @@ -525,7 +539,7 @@ export class ListView implements ISpliceable, IDisposable { } } - private onDragEnd(): void { + private _onDragEnd(): void { this.dragOverAnimationStopDisposable.dispose(); if (this.dragOverAnimationDisposable) { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index e83d5aa2164..8e839278ac1 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, IDragAndDrop, IDragAndDropData } from './list'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IDragAndDrop, IDragAndDropData, IListDragEvent } from './list'; import { ListView, IListViewOptions } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin, getOrDefault } from 'vs/base/common/objects'; @@ -1154,6 +1154,9 @@ export class List implements ISpliceable, IDisposable { this.view.onDragStart(this.onDragStart, this, this.disposables); + this.view.onDragOver(this.onDragOver, this, this.disposables); + this.view.onDragLeave(this.onDragLeave, this, this.disposables); + this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); @@ -1471,6 +1474,14 @@ export class List implements ISpliceable, IDisposable { this.dnd.onDragStart(this.currentDragAndDropData, event); } + private onDragOver(event: IListDragEvent): void { + console.log(event); + } + + private onDragLeave(): void { + console.log('LEAVE'); + } + dispose(): void { this._onDidDispose.fire(); this.disposables = dispose(this.disposables); From f7894236eea20569aca7c7e64390c0d300db8b77 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 7 Jan 2019 16:52:04 -0800 Subject: [PATCH 0081/1066] css.experimental.customData --- .../.vscode/settings.json | 2 + .../client/src/cssMain.ts | 19 ++++++++- extensions/css-language-features/package.json | 4 ++ .../server/src/cssServerMain.ts | 41 ++++++++++++++++--- .../server/src/languageFacts.ts | 21 ++++++++++ 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 extensions/css-language-features/.vscode/settings.json create mode 100644 extensions/css-language-features/server/src/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..7a73a41bfdf --- /dev/null +++ b/extensions/css-language-features/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 6e0f2eb77c7..fbbec3cee86 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -9,7 +9,7 @@ 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 } from 'vscode'; +import { languages, window, commands, ExtensionContext, Range, Position, CompletionItem, CompletionItemKind, TextEdit, SnippetString, workspace } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind, Disposable } from 'vscode-languageclient'; // this method is called when vs code is activated @@ -30,6 +30,22 @@ export function activate(context: ExtensionContext) { let documentSelector = ['css', 'scss', 'less']; + let dataPaths: string[] = workspace.getConfiguration('css').get('experimental.customData', []); + if (dataPaths && dataPaths.length > 0) { + if (!workspace.workspaceFolders) { + dataPaths = []; + } else { + try { + const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath; + dataPaths = dataPaths.map(d => { + return path.resolve(workspaceRoot, d); + }); + } catch (err) { + dataPaths = []; + } + } + } + // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -37,6 +53,7 @@ export function activate(context: ExtensionContext) { configurationSection: ['css', 'scss', 'less'] }, initializationOptions: { + dataPaths } }; diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index e6f71ff0260..4583d0b2f65 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -32,6 +32,10 @@ "id": "css", "title": "%css.title%", "properties": { + "css.experimental.customData": { + "type": "array", + "description": "A list of JSON file paths that define custom CSS data that loads extra properties, at directives, pseudo classes / elements." + }, "css.validate": { "type": "boolean", "scope": "resource", diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index 8c516fb8598..e376eaec816 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -7,6 +7,7 @@ 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 } from 'vscode-languageserver-types'; import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; @@ -14,6 +15,7 @@ import { getLanguageModelCache } from './languageModelCache'; import { getPathCompletionParticipant } from './pathCompletion'; import { formatError, runSafe } from './utils/runner'; import { getDocumentContext } from './utils/documentContext'; +import { parseCSSData } from './languageFacts'; export interface Settings { css: LanguageSettings; @@ -50,6 +52,8 @@ let scopedSettingsSupport = false; let foldingRangeLimit = Number.MAX_VALUE; let workspaceFolders: WorkspaceFolder[]; +const languageServices: { [id: string]: LanguageService } = {}; + // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { @@ -61,6 +65,33 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } + const dataPaths: string[] = params.initializationOptions.dataPaths; + + let customData = { + customProperties: [], + customAtDirectives: [], + customPseudoElements: [], + customPseudoClasses: [] + }; + + dataPaths.forEach(p => { + if (fs.existsSync(p)) { + const { + properties, + atDirectives, + pseudoClasses, + pseudoElements + } = parseCSSData(fs.readFileSync(p, 'utf-8')); + + customData.customProperties = customData.customProperties.concat(properties); + customData.customAtDirectives = customData.customAtDirectives.concat(atDirectives); + customData.customPseudoClasses = customData.customPseudoClasses.concat(pseudoClasses); + customData.customPseudoElements = customData.customPseudoElements.concat(pseudoElements); + } else { + return; + } + }); + function getClientCapability(name: string, def: T) { const keys = name.split('.'); let c: any = params.capabilities; @@ -76,6 +107,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); + languageServices.css = getCSSLanguageService(customData); + languageServices.scss = getSCSSLanguageService(customData); + languageServices.less = getLESSLanguageService(customData); + const capabilities: ServerCapabilities = { // Tell the client that the server works in FULL text document sync mode textDocumentSync: documents.syncKind, @@ -96,12 +131,6 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { return { capabilities }; }); -const languageServices: { [id: string]: LanguageService } = { - css: getCSSLanguageService(), - scss: getSCSSLanguageService(), - less: getLESSLanguageService() -}; - function getLanguageService(document: TextDocument) { let service = languageServices[document.languageId]; if (!service) { diff --git a/extensions/css-language-features/server/src/languageFacts.ts b/extensions/css-language-features/server/src/languageFacts.ts new file mode 100644 index 00000000000..3e2061101e3 --- /dev/null +++ b/extensions/css-language-features/server/src/languageFacts.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function parseCSSData(source: string) { + let rawData: any; + + try { + rawData = JSON.parse(source); + } catch (err) { + return {}; + } + + return { + properties: rawData.properties || [], + atDirectives: rawData.atdirectives || [], + pseudoClasses: rawData.pseudoclasses || [], + pseudoElements: rawData.pseudoelements || [] + }; +} From 9d8216aecd133ca3649e73e1349f067b30dcdd94 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 8 Jan 2019 12:43:12 -0800 Subject: [PATCH 0082/1066] Remove extra settings.json --- extensions/css-language-features/.vscode/settings.json | 2 -- extensions/css-language-features/package.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 extensions/css-language-features/.vscode/settings.json diff --git a/extensions/css-language-features/.vscode/settings.json b/extensions/css-language-features/.vscode/settings.json deleted file mode 100644 index 7a73a41bfdf..00000000000 --- a/extensions/css-language-features/.vscode/settings.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 4583d0b2f65..43c2fcb26f7 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -34,7 +34,7 @@ "properties": { "css.experimental.customData": { "type": "array", - "description": "A list of JSON file paths that define custom CSS data that loads extra properties, at directives, pseudo classes / elements." + "description": "A list of JSON file paths that define custom CSS data that loads custom properties, at directives, pseudo classes / elements." }, "css.validate": { "type": "boolean", From a86c0330216b558333e5d92a0ae4a5a5b6823c04 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 14:52:56 -0800 Subject: [PATCH 0083/1066] Strict null check actions --- src/tsconfig.strictNullChecks.json | 1 + .../editor/common/services/getIconClasses.ts | 2 +- src/vs/workbench/electron-browser/actions.ts | 31 ++++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index f1f1864b7ea..4ebe71cf7d2 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -571,6 +571,7 @@ "./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/resources.ts", "./vs/workbench/electron-browser/window.ts", "./vs/workbench/parts/backup/common/backupRestorer.ts", diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index 7861390e05b..09da432e4f2 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -11,7 +11,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IModelService } from 'vs/editor/common/services/modelService'; import { FileKind } from 'vs/platform/files/common/files'; -export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri, fileKind?: FileKind): string[] { +export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri | undefined, fileKind?: FileKind): string[] { // we always set these base classes even if we do not have a path const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon']; if (resource) { diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 1dafd0865db..6db3ea21792 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -150,7 +150,7 @@ export class ToggleMenuBarAction extends Action { this.configurationService.updateValue(ToggleMenuBarAction.menuBarVisibilityKey, newVisibilityValue, ConfigurationTarget.USER); - return Promise.resolve(null); + return Promise.resolve(); } } @@ -431,22 +431,22 @@ export abstract class BaseOpenRecentAction extends Action { private openRecent(recentWorkspaces: Array, recentFiles: URI[]): void { - const toPick = (workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, fileKind: FileKind, labelService: ILabelService, buttons: IQuickInputButton[]) => { + 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)); + description = labelService.getUriLabel(dirname(resource)!); } else if (isWorkspaceIdentifier(workspace)) { resource = URI.file(workspace.configPath); label = labelService.getWorkspaceLabel(workspace); - description = labelService.getUriLabel(dirname(resource)); + description = labelService.getUriLabel(dirname(resource)!); } else { resource = workspace; label = getBaseLabel(workspace); - description = labelService.getUriLabel(dirname(resource)); + description = labelService.getUriLabel(dirname(resource)!); } return { @@ -486,11 +486,10 @@ export abstract class BaseOpenRecentAction extends Action { this.windowsService.removeFromRecentlyOpened([context.item.workspace]).then(() => context.removeItem()); } }) - .then(pick => { + .then((pick): Promise | void => { if (pick) { return runPick(pick.resource, pick.fileKind === FileKind.FILE, keyMods); } - return null; }); } } @@ -618,7 +617,7 @@ export class KeybindingsReferenceAction extends Action { run(): Promise { window.open(KeybindingsReferenceAction.URL); - return null; + return Promise.resolve(); } } @@ -639,7 +638,7 @@ export class OpenDocumentationUrlAction extends Action { run(): Promise { window.open(OpenDocumentationUrlAction.URL); - return null; + return Promise.resolve(); } } @@ -660,7 +659,7 @@ export class OpenIntroductoryVideosUrlAction extends Action { run(): Promise { window.open(OpenIntroductoryVideosUrlAction.URL); - return null; + return Promise.resolve(); } } @@ -681,7 +680,7 @@ export class OpenTipsAndTricksUrlAction extends Action { run(): Promise { window.open(OpenTipsAndTricksUrlAction.URL); - return null; + return Promise.resolve(); } } @@ -970,7 +969,7 @@ export abstract class BaseResizeViewAction extends Action { const isSidebarFocus = this.partService.hasFocus(Parts.SIDEBAR_PART); const isPanelFocus = this.partService.hasFocus(Parts.PANEL_PART); - let part: Parts; + let part: Parts | undefined; if (isSidebarFocus) { part = Parts.SIDEBAR_PART; } else if (isPanelFocus) { @@ -1264,7 +1263,11 @@ export class InspectContextKeysAction extends Action { const disposables: IDisposable[] = []; const stylesheet = createStyleSheet(); - disposables.push(toDisposable(() => stylesheet.parentNode.removeChild(stylesheet))); + disposables.push(toDisposable(() => { + if (stylesheet.parentNode) { + stylesheet.parentNode.removeChild(stylesheet); + } + })); createCSSRule('*', 'cursor: crosshair !important;', stylesheet); const hoverFeedback = document.createElement('div'); @@ -1302,7 +1305,7 @@ export class InspectContextKeysAction extends Action { dispose(disposables); }, null, disposables); - return Promise.resolve(null); + return Promise.resolve(); } } From e053ead5e7a3234f7dfcbafeb53a526c812e1357 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 14:57:15 -0800 Subject: [PATCH 0084/1066] Strict null check sidebarPart --- src/tsconfig.strictNullChecks.json | 1 + src/vs/workbench/browser/parts/sidebar/sidebarPart.ts | 6 +++--- src/vs/workbench/electron-browser/actions.ts | 3 ++- src/vs/workbench/services/viewlet/browser/viewlet.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 4ebe71cf7d2..0831c8641b1 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -548,6 +548,7 @@ "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", "./vs/workbench/browser/parts/quickopen/quickopen.ts", + "./vs/workbench/browser/parts/sidebar/sidebarPart.ts", "./vs/workbench/browser/parts/statusbar/statusbar.ts", "./vs/workbench/browser/parts/statusbar/statusbarPart.ts", "./vs/workbench/browser/parts/views/panelViewlet.ts", diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 5847a12b0a0..0d03a8851f7 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -175,7 +175,7 @@ export class SidebarPart extends CompositePart implements IViewletServi } } - openViewlet(id: string, focus?: boolean): Promise { + openViewlet(id: string, focus?: boolean): Promise { if (this.getViewlet(id)) { return Promise.resolve(this.doOpenViewlet(id, focus)); } @@ -195,7 +195,7 @@ export class SidebarPart extends CompositePart implements IViewletServi getAllViewlets(): ViewletDescriptor[] { return this.viewletRegistry.getViewlets() - .sort((v1, v2) => v1.order - v2.order); + .sort((v1, v2) => v1.order! - v2.order!); } getDefaultViewletId(): string { @@ -206,7 +206,7 @@ export class SidebarPart extends CompositePart implements IViewletServi return this.getViewlets().filter(viewlet => viewlet.id === id)[0]; } - private doOpenViewlet(id: string, focus?: boolean): Viewlet { + private doOpenViewlet(id: string, focus?: boolean): Viewlet | null { if (this.blockOpeningViewlet) { return null; // Workaround against a potential race condition } diff --git a/src/vs/workbench/electron-browser/actions.ts b/src/vs/workbench/electron-browser/actions.ts index 6db3ea21792..a2538660366 100644 --- a/src/vs/workbench/electron-browser/actions.ts +++ b/src/vs/workbench/electron-browser/actions.ts @@ -768,7 +768,8 @@ export abstract class BaseNavigationAction extends Action { const activeViewletId = this.viewletService.getActiveViewlet().getId(); - return this.viewletService.openViewlet(activeViewletId, true); + return this.viewletService.openViewlet(activeViewletId, true) + .then(value => value === null ? false : value); } protected navigateAcrossEditorGroup(direction: GroupDirection): boolean { diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index 7ae5e2acc4f..b03de9f75f3 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -22,7 +22,7 @@ export interface IViewletService { /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. */ - openViewlet(id: string, focus?: boolean): Promise; + openViewlet(id: string, focus?: boolean): Promise; /** * Returns the current active viewlet or null if none. From d00ce850129d835d3506ff82672c61a1dea9cb91 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 15:00:45 -0800 Subject: [PATCH 0085/1066] Strict null check timerService --- src/tsconfig.strictNullChecks.json | 3 +- .../timer/electron-browser/timerService.ts | 42 +++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 0831c8641b1..314dc6c37a6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -866,7 +866,8 @@ "./vs/workbench/test/common/editor/editorOptions.test.ts", "./vs/workbench/test/common/notifications.test.ts", "./vs/workbench/test/electron-browser/api/extHostTypes.test.ts", - "./vs/workbench/test/electron-browser/api/mock.ts" + "./vs/workbench/test/electron-browser/api/mock.ts", + "./vs/workbench/services/timer/electron-browser/timerService.ts" ], "exclude": [ "./typings/require-monaco.d.ts" diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index 1ef78a13d9e..c694be89eed 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -121,12 +121,12 @@ export interface IStartupMetrics { /** * The active viewlet id or `undedined` */ - viewletId: string; + viewletId?: string; /** * The active panel id or `undefined` */ - panelId: string; + panelId?: string; /** * The editor input types or `[]` @@ -173,7 +173,7 @@ export interface IStartupMetrics { * * This only happens when a non-english locale is being used. * * It is our code running here and we should monitor this carefully for regressions. */ - ellapsedNlsGeneration: number; + ellapsedNlsGeneration?: number; /** * The time it took to tell electron to open/restore a renderer (browser window). @@ -317,15 +317,15 @@ export interface IStartupMetrics { }; hasAccessibilitySupport: boolean; - isVMLikelyhood: number; - platform: string; - release: string; - arch: string; - totalmem: number; - freemem: number; - meminfo: IMemoryInfo; - cpus: { count: number; speed: number; model: string; }; - loadavg: number[]; + isVMLikelyhood?: number; + platform?: string; + release?: string; + arch?: string; + totalmem?: number; + freemem?: number; + meminfo?: IMemoryInfo; + cpus?: { count: number; speed: number; model: string; }; + loadavg?: number[]; } export interface ITimerService { @@ -367,15 +367,15 @@ class TimerService implements ITimerService { const initialStartup = !!this._windowService.getConfiguration().isInitialStartup; const startMark = initialStartup ? 'main:started' : 'main:loadWindow'; - let totalmem: number; - let freemem: number; - let cpus: { count: number; speed: number; model: string; }; - let platform: string; - let release: string; - let arch: string; - let loadavg: number[]; - let meminfo: IMemoryInfo; - let isVMLikelyhood: number; + let totalmem: number | undefined; + let freemem: number | undefined; + let cpus: { count: number; speed: number; model: string; } | undefined; + let platform: string | undefined; + let release: string | undefined; + let arch: string | undefined; + let loadavg: number[] | undefined; + let meminfo: IMemoryInfo | undefined; + let isVMLikelyhood: number | undefined; try { totalmem = os.totalmem(); From 53883bd51562430b4d49591fd662da1ae40bb73c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 15:02:20 -0800 Subject: [PATCH 0086/1066] Mark fields readonly --- .../timer/electron-browser/timerService.ts | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index c694be89eed..c4a524d3706 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -30,10 +30,10 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic } */ export interface IMemoryInfo { - workingSetSize: number; - peakWorkingSetSize: number; - privateBytes: number; - sharedBytes: number; + readonly workingSetSize: number; + readonly peakWorkingSetSize: number; + readonly privateBytes: number; + readonly sharedBytes: number; } /* __GDPR__FRAGMENT__ @@ -85,53 +85,53 @@ export interface IStartupMetrics { /** * The version of these metrics. */ - version: 2; + readonly version: 2; /** * If this started the main process and renderer or just a renderer (new or reloaded). */ - initialStartup: boolean; + readonly initialStartup: boolean; /** * No folder, no file, no workspace has been opened */ - emptyWorkbench: boolean; + readonly emptyWorkbench: boolean; /** * This is the latest (stable/insider) version. Iff not we should ignore this * measurement. */ - isLatestVersion: boolean; + readonly isLatestVersion: boolean; /** * Whether we asked for and V8 accepted cached data. */ - didUseCachedData: boolean; + readonly didUseCachedData: boolean; /** * How/why the window was created. See https://github.com/Microsoft/vscode/blob/d1f57d871722f4d6ba63e4ef6f06287121ceb045/src/vs/platform/lifecycle/common/lifecycle.ts#L50 */ - windowKind: number; + readonly windowKind: number; /** * The total number of windows that have been restored/created */ - windowCount: number; + readonly windowCount: number; /** * The active viewlet id or `undedined` */ - viewletId?: string; + readonly viewletId?: string; /** * The active panel id or `undefined` */ - panelId?: string; + readonly panelId?: string; /** * The editor input types or `[]` */ - editorIds: string[]; + readonly editorIds: string[]; /** * The time it took to create the workbench. @@ -146,12 +146,12 @@ export interface IStartupMetrics { * * The numbers of windows being restored (when starting 'fresh') * * The viewlet being restored (esp. when it's a contributed viewlet) */ - ellapsed: number; + readonly ellapsed: number; /** * Individual timers... */ - timers: { + readonly timers: { /** * The time it took to receieve the [`ready`](https://electronjs.org/docs/api/app#event-ready)-event. Measured from the first line * of JavaScript code till receiving that event. @@ -163,7 +163,7 @@ export interface IStartupMetrics { * * This is often affected by AV software (and can change with AV software updates outside of our release-cycle). * * It is not our code running here and we can only observe what's happening. */ - ellapsedAppReady?: number; + readonly ellapsedAppReady?: number; /** * The time it took to generate NLS data. @@ -173,7 +173,7 @@ export interface IStartupMetrics { * * This only happens when a non-english locale is being used. * * It is our code running here and we should monitor this carefully for regressions. */ - ellapsedNlsGeneration?: number; + readonly ellapsedNlsGeneration?: number; /** * The time it took to tell electron to open/restore a renderer (browser window). @@ -183,7 +183,7 @@ export interface IStartupMetrics { * * This can be compared between insider and stable builds. * * It is our code running here and we should monitor this carefully for regressions. */ - ellapsedWindowLoad?: number; + readonly ellapsedWindowLoad?: number; /** * The time it took to create a new renderer (browser window) and to initialize that to the point @@ -195,7 +195,7 @@ export interface IStartupMetrics { * * It is mostly not our code running here and we can only observe what's happening. * */ - ellapsedWindowLoadToRequire: number; + readonly ellapsedWindowLoadToRequire: number; /** * The time it took to require the global storage DB, connect to it @@ -204,7 +204,7 @@ export interface IStartupMetrics { * * Happens in the main-process * * Measured with the `main:willInitGlobalStorage` and `main:didInitGlobalStorage` performance marks. */ - ellapsedGlobalStorageInitMain: number; + readonly ellapsedGlobalStorageInitMain: number; /** * The time it took to load the initial set of values from the global storage. @@ -212,7 +212,7 @@ export interface IStartupMetrics { * * Happens in the renderer-process * * Measured with the `willInitGlobalStorage` and `didInitGlobalStorage` performance marks. */ - ellapsedGlobalStorageInitRenderer: number; + readonly ellapsedGlobalStorageInitRenderer: number; /** * The time it took to require the workspace storage DB. @@ -220,7 +220,7 @@ export interface IStartupMetrics { * * Happens in the renderer-process * * Measured with the `willRequireSQLite` and `didRequireSQLite` performance marks. */ - ellapsedWorkspaceStorageRequire: number; + readonly ellapsedWorkspaceStorageRequire: number; /** * The time it took to require the workspace storage DB, connect to it @@ -229,7 +229,7 @@ export interface IStartupMetrics { * * Happens in the renderer-process * * Measured with the `willInitWorkspaceStorage` and `didInitWorkspaceStorage` performance marks. */ - ellapsedWorkspaceStorageInit: number; + readonly ellapsedWorkspaceStorageInit: number; /** * The time it took to initialize the workspace and configuration service. @@ -237,7 +237,7 @@ export interface IStartupMetrics { * * Happens in the renderer-process * * Measured with the `willInitWorkspaceService` and `didInitWorkspaceService` performance marks. */ - ellapsedWorkspaceServiceInit: number; + readonly ellapsedWorkspaceServiceInit: number; /** * The time it took to load the main-bundle of the workbench, e.g `workbench.main.js`. @@ -248,7 +248,7 @@ export interface IStartupMetrics { * * This should be looked at with and without V8 cached data usage and per electron/v8 version * * This is affected by the size of our code bundle (which grows about 3-5% per release) */ - ellapsedRequire: number; + readonly ellapsedRequire: number; /** * The time it took to read extensions' package.json-files *and* interpret them (invoking @@ -262,11 +262,11 @@ export interface IStartupMetrics { * * todo@joh/ramya this measures an artifical dealy we have added, see https://github.com/Microsoft/vscode/blob/2f07ddae8bf56e969e3f4ba1447258ebc999672f/src/vs/workbench/services/extensions/electron-browser/extensionService.ts#L311-L326 */ - ellapsedExtensions: number; + readonly ellapsedExtensions: number; // the time from start till `didLoadExtensions` // remove? - ellapsedExtensionsReady: number; + readonly ellapsedExtensionsReady: number; /** * The time it took to restore the viewlet. @@ -276,7 +276,7 @@ export interface IStartupMetrics { * * This should be looked at per viewlet-type/id. * * Happens in parallel to other things, depends on async timing */ - ellapsedViewletRestore: number; + readonly ellapsedViewletRestore: number; /** * The time it took to restore the panel. @@ -286,7 +286,7 @@ export interface IStartupMetrics { * * This should be looked at per panel-type/id. * * Happens in parallel to other things, depends on async timing */ - ellapsedPanelRestore: number; + readonly ellapsedPanelRestore: number; /** * The time it took to restore editors - that is text editor and complex editor likes the settings UI @@ -299,7 +299,7 @@ export interface IStartupMetrics { * * todo@joh/ramya We should probably measures each editor individually? */ - ellapsedEditorRestore: number; + readonly ellapsedEditorRestore: number; /** * The time it took to create the workbench. @@ -309,23 +309,23 @@ export interface IStartupMetrics { * * todo@joh/ramya Not sure if this is useful because this includes too much */ - ellapsedWorkbench: number; + readonly ellapsedWorkbench: number; // the time it took to generate this object. // remove? - ellapsedTimersToTimersComputed: number; + readonly ellapsedTimersToTimersComputed: number; }; - hasAccessibilitySupport: boolean; - isVMLikelyhood?: number; - platform?: string; - release?: string; - arch?: string; - totalmem?: number; - freemem?: number; - meminfo?: IMemoryInfo; - cpus?: { count: number; speed: number; model: string; }; - loadavg?: number[]; + readonly hasAccessibilitySupport: boolean; + readonly isVMLikelyhood?: number; + readonly platform?: string; + readonly release?: string; + readonly arch?: string; + readonly totalmem?: number; + readonly freemem?: number; + readonly meminfo?: IMemoryInfo; + readonly cpus?: { count: number; speed: number; model: string; }; + readonly loadavg?: number[]; } export interface ITimerService { @@ -349,8 +349,7 @@ class TimerService implements ITimerService { @IViewletService private readonly _viewletService: IViewletService, @IPanelService private readonly _panelService: IPanelService, @IEditorService private readonly _editorService: IEditorService, - ) { - } + ) { } get startupMetrics(): Promise { if (!this._startupMetrics) { From 6860cc63d812ecc0464a93996ae6779323b081d8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 15:04:50 -0800 Subject: [PATCH 0087/1066] Strict null check startupTimings --- src/tsconfig.strictNullChecks.json | 5 +++-- .../parts/performance/electron-browser/startupTimings.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 314dc6c37a6..bae651862fb 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -640,6 +640,7 @@ "./vs/workbench/parts/output/common/output.ts", "./vs/workbench/parts/output/common/outputLinkComputer.ts", "./vs/workbench/parts/output/common/outputLinkProvider.ts", + "./vs/workbench/parts/performance/electron-browser/startupTimings.ts", "./vs/workbench/parts/preferences/browser/settingsWidgets.ts", "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", @@ -858,6 +859,7 @@ "./vs/workbench/services/themes/common/workbenchThemeService.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeData.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts", + "./vs/workbench/services/timer/electron-browser/timerService.ts", "./vs/workbench/services/title/common/titleService.ts", "./vs/workbench/services/viewlet/browser/viewlet.ts", "./vs/workbench/services/workspace/common/workspaceEditing.ts", @@ -866,8 +868,7 @@ "./vs/workbench/test/common/editor/editorOptions.test.ts", "./vs/workbench/test/common/notifications.test.ts", "./vs/workbench/test/electron-browser/api/extHostTypes.test.ts", - "./vs/workbench/test/electron-browser/api/mock.ts", - "./vs/workbench/services/timer/electron-browser/timerService.ts" + "./vs/workbench/test/electron-browser/api/mock.ts" ], "exclude": [ "./typings/require-monaco.d.ts" diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index ba77db5110e..c8ea72f9518 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -87,7 +87,7 @@ export class StartupTimings implements IWorkbenchContribution { this._timerService.startupMetrics, waitWhenNoCachedData(), ]).then(([startupMetrics]) => { - return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${product.commit.slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`); + return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${(product.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`); }).then(() => { this._windowsService.quit(); }).catch(err => { From 6eeb11ecf204869741faad12979f1709dbdbddd8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 15:53:11 -0800 Subject: [PATCH 0088/1066] Extract updateConfig --- .../src/test/completions.test.ts | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index 5ba801de824..9400854b21c 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -10,35 +10,41 @@ import { disposeAll } from '../utils/dispose'; const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); -const configOverrides: { readonly [key: string]: any } = Object.freeze({ - 'editor.suggestSelection': 'first', - 'typescript.suggest.completeFunctionCalls': false, -}); +type VsCodeConfiguration = { [key: string]: any }; + + +async function updateConfig(newConfig: VsCodeConfiguration): Promise { + const oldConfig: VsCodeConfiguration = {}; + const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); + for (const configKey of Object.keys(newConfig)) { + oldConfig[configKey] = config.get(configKey); + await new Promise((resolve, reject) => config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global).then(() => resolve(), reject)); + } + return oldConfig; +} + suite('TypeScript Completions', () => { + const configDefaults: VsCodeConfiguration = Object.freeze({ + 'editor.suggestSelection': 'first', + 'typescript.suggest.completeFunctionCalls': false, + }); + const _disposables: vscode.Disposable[] = []; let oldConfig: { [key: string]: any } = {}; setup(async () => { await wait(100); - // save off config and update overrides - oldConfig = {}; - const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); - for (const configKey of Object.keys(configOverrides)) { - oldConfig[configKey] = config.get(configKey); - await new Promise((resolve, reject) => config.update(configKey, configOverrides[configKey], vscode.ConfigurationTarget.Global).then(() => resolve(), reject)); - } + // Save off config and apply defaults + oldConfig = await updateConfig(configDefaults); }); teardown(async () => { disposeAll(_disposables); // Restore config - const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); - for (const configKey of Object.keys(oldConfig)) { - await new Promise((resolve, reject) => config.update(configKey, oldConfig[configKey], vscode.ConfigurationTarget.Global).then(() => resolve(), reject)); - } + await updateConfig(oldConfig); return vscode.commands.executeCommand('workbench.action.closeAllEditors'); }); From f49c40947ed3d2d5fb62f0c88431cd395f0362a5 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 16:07:40 -0800 Subject: [PATCH 0089/1066] Add test for completeFunctionCalls --- .../src/test/completions.test.ts | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index 9400854b21c..40156ffd8b4 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -12,22 +12,27 @@ const testDocumentUri = vscode.Uri.parse('untitled:test.ts'); type VsCodeConfiguration = { [key: string]: any }; - async function updateConfig(newConfig: VsCodeConfiguration): Promise { const oldConfig: VsCodeConfiguration = {}; const config = vscode.workspace.getConfiguration(undefined, testDocumentUri); for (const configKey of Object.keys(newConfig)) { oldConfig[configKey] = config.get(configKey); - await new Promise((resolve, reject) => config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global).then(() => resolve(), reject)); + await new Promise((resolve, reject) => + config.update(configKey, newConfig[configKey], vscode.ConfigurationTarget.Global) + .then(() => resolve(), reject)); } return oldConfig; } +namespace Config { + export const suggestSelection = 'editor.suggestSelection'; + export const completeFunctionCalls = 'typescript.suggest.completeFunctionCalls'; +} suite('TypeScript Completions', () => { const configDefaults: VsCodeConfiguration = Object.freeze({ - 'editor.suggestSelection': 'first', - 'typescript.suggest.completeFunctionCalls': false, + [Config.suggestSelection]: 'first', + [Config.completeFunctionCalls]: false, }); const _disposables: vscode.Disposable[] = []; @@ -182,6 +187,25 @@ suite('TypeScript Completions', () => { `abc` )); }); + + test('completeFunctionCalls should complete function parameters', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function foo(x, y, z) { }`, + `foo$0` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function foo(x, y, z) { }`, + `foo(x, y, z)` + )); + }); }); const joinLines = (...args: string[]) => args.join('\n'); @@ -193,6 +217,9 @@ async function acceptFirstSuggestion(uri: vscode.Uri, _disposables: vscode.Dispo const didSuggest = onDidSuggest(_disposables); await vscode.commands.executeCommand('editor.action.triggerSuggest'); await didSuggest; + // TODO: depends on reverting fix for https://github.com/Microsoft/vscode/issues/64257 + // Make sure we have time to resolve the suggestion because `acceptSelectedSuggestion` doesn't + await wait(40); await vscode.commands.executeCommand('acceptSelectedSuggestion'); return await didChangeDocument; } From e44801f2b3ab00c26c742aa0ef473824469fad02 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 8 Jan 2019 16:25:07 -0800 Subject: [PATCH 0090/1066] Fix inserting of duplicate calls when using `typescript.suggest.completeFunctionCalls ` Fixes #18131 --- .../src/features/completions.ts | 15 ++-- .../src/test/completions.test.ts | 68 +++++++++++++++++-- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index fc7037a429c..a8a6040c5ae 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -480,7 +480,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider item.additionalTextEdits = codeAction.additionalTextEdits; if (detail && item.useCodeSnippet) { - const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, token); + const shouldCompleteFunction = await this.isValidFunctionCompletionContext(filepath, item.position, item.document, token); if (shouldCompleteFunction) { const { snippet, parameterCount } = snippetForFunctionCall(item, detail.displayParts); item.insertText = snippet; @@ -630,6 +630,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider private async isValidFunctionCompletionContext( filepath: string, position: vscode.Position, + document: vscode.TextDocument, token: vscode.CancellationToken ): Promise { // Workaround for https://github.com/Microsoft/TypeScript/issues/12677 @@ -637,23 +638,25 @@ 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') { + if (response.type !== 'response' || !response.body) { return true; } - const { body } = response; - switch (body && body.kind) { + switch (response.body.kind) { case 'var': case 'let': case 'const': case 'alias': return false; - default: - return true; } } catch (e) { return true; } + + // Don't complete function call if there is already something that looks like a funciton call + // https://github.com/Microsoft/vscode/issues/18131 + const after = document.lineAt(position.line).text.slice(position.character); + return after.match(/^\s*\(/g) === null; } } diff --git a/extensions/typescript-language-features/src/test/completions.test.ts b/extensions/typescript-language-features/src/test/completions.test.ts index 40156ffd8b4..563930d78d5 100644 --- a/extensions/typescript-language-features/src/test/completions.test.ts +++ b/extensions/typescript-language-features/src/test/completions.test.ts @@ -188,22 +188,80 @@ suite('TypeScript Completions', () => { )); }); - test('completeFunctionCalls should complete function parameters', async () => { + test('completeFunctionCalls should complete function parameters when at end of word', async () => { await updateConfig({ [Config.completeFunctionCalls]: true, }); + // Complete with-in word await createTestEditor(testDocumentUri, - `function foo(x, y, z) { }`, - `foo$0` + `function abcdef(x, y, z) { }`, + `abcdef$0` ); const document = await acceptFirstSuggestion(testDocumentUri, _disposables); assert.strictEqual( document.getText(), joinLines( - `function foo(x, y, z) { }`, - `foo(x, y, z)` + `function abcdef(x, y, z) { }`, + `abcdef(x, y, z)` + )); + }); + + test.skip('completeFunctionCalls should complete function parameters when within word', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcd$0ef` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(x, y, z)` + )); + }); + + test('completeFunctionCalls should not complete function parameters at end of word if we are already in something that looks like a function call, #18131', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcdef$0(1, 2, 3)` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(1, 2, 3)` + )); + }); + + test.skip('completeFunctionCalls should not complete function parameters within word if we are already in something that looks like a function call, #18131', async () => { + await updateConfig({ + [Config.completeFunctionCalls]: true, + }); + + await createTestEditor(testDocumentUri, + `function abcdef(x, y, z) { }`, + `abcd$0ef(1, 2, 3)` + ); + + const document = await acceptFirstSuggestion(testDocumentUri, _disposables); + assert.strictEqual( + document.getText(), + joinLines( + `function abcdef(x, y, z) { }`, + `abcdef(1, 2, 3)` )); }); }); From a7149b9fff919b5c525bdd33eab81bf31232a8b6 Mon Sep 17 00:00:00 2001 From: jamesgeorge007 Date: Mon, 7 Jan 2019 23:46:25 +0530 Subject: [PATCH 0091/1066] Add multiline description support #66036 update schema --- .../parts/snippets/electron-browser/snippets.contribution.ts | 2 +- .../workbench/parts/snippets/electron-browser/snippetsFile.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts index b37fb9aff88..7683f6b1b3a 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts @@ -51,7 +51,7 @@ const languageScopeSchema: IJSONSchema = { }, description: { description: nls.localize('snippetSchema.json.description', 'The snippet description.'), - type: 'string' + type: ['string', 'array'] } }, additionalProperties: false diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts index e09956dae4e..d1fb6e838d5 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts @@ -232,6 +232,10 @@ export class SnippetFile { body = body.join('\n'); } + if (Array.isArray(description)) { + description = description.join('\n'); + } + if ((typeof prefix !== 'string' && !Array.isArray(prefix)) || typeof body !== 'string') { return; } From 46d1ac2bbc926ac1efc408fafa563e34df5e69a8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Jan 2019 08:36:45 +0100 Subject: [PATCH 0092/1066] Add "Measure Extension Host Latency" action --- src/vs/workbench/api/node/extHost.protocol.ts | 4 + .../api/node/extHostExtensionService.ts | 14 ++ .../extensionHostProcessManager.ts | 127 ++++++++++++++++++ 3 files changed, 145 insertions(+) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 5fcf6bc7b04..9bd72240a46 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -739,6 +739,10 @@ export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; $activateByEvent(activationEvent: string): Promise; + + $test_latency(n: number): Promise; + $test_up(b: Buffer): Promise; + $test_down(size: number): Promise; } export interface FileSystemEvents { diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 13a4218c684..2cf4f9efeed 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -646,6 +646,20 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { .then(_ => this._activateByEvent(activationEvent, false)) ); } + + public async $test_latency(n: number): Promise { + return n; + } + + public async $test_up(b: Buffer): Promise { + return b.length; + } + + public async $test_down(size: number): Promise { + let b = Buffer.alloc(size, Math.random() % 256); + return b; + } + } function loadCommonJSModule(logService: ILogService, modulePath: string, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index b7f836c30e3..ba1ad93bebd 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -19,6 +19,14 @@ import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIden import { IRPCProtocolLogger, RPCProtocol, RequestInitiator, ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import * as nls from 'vs/nls'; +import { Action } from 'vs/base/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IUntitledResourceInput } from 'vs/workbench/common/editor'; +import { StopWatch } from 'vs/base/common/stopwatch'; // Enable to see detailed message communication between window and extension host const LOG_EXTENSION_HOST_COMMUNICATION = false; @@ -71,6 +79,9 @@ export class ExtensionHostProcessManager extends Disposable { ); this._extensionHostProcessProxy.then(() => { initialActivationEvents.forEach((activationEvent) => this.activateByEvent(activationEvent)); + this._register(registerLatencyTestProvider({ + measure: () => this.measure() + })); }); } @@ -94,6 +105,57 @@ export class ExtensionHostProcessManager extends Disposable { super.dispose(); } + private async measure(): Promise { + const latency = await this._measureLatency(); + const down = await this._measureDown(); + const up = await this._measureUp(); + return { + remoteAuthority: this._remoteAuthority, + latency, + down, + up + }; + } + + private async _measureLatency(): Promise { + const COUNT = 10; + + const { value: proxy } = await this._extensionHostProcessProxy; + let sum = 0; + for (let i = 0; i < COUNT; i++) { + const sw = StopWatch.create(true); + await proxy.$test_latency(i); + sw.stop(); + sum += sw.elapsed(); + } + return (sum / COUNT); + } + + private static _convert(byteCount: number, elapsedMillis: number): number { + return (byteCount * 1000 * 8) / elapsedMillis; + } + + private async _measureUp(): Promise { + const SIZE = 10 * 1024 * 1024; // 10MB + + const { value: proxy } = await this._extensionHostProcessProxy; + let b = Buffer.alloc(SIZE, Math.random() % 256); + const sw = StopWatch.create(true); + await proxy.$test_up(b); + sw.stop(); + return ExtensionHostProcessManager._convert(SIZE, sw.elapsed()); + } + + private async _measureDown(): Promise { + const SIZE = 10 * 1024 * 1024; // 10MB + + const { value: proxy } = await this._extensionHostProcessProxy; + const sw = StopWatch.create(true); + await proxy.$test_down(SIZE); + sw.stop(); + return ExtensionHostProcessManager._convert(SIZE, sw.elapsed()); + } + public canProfileExtensionHost(): boolean { return this._extensionHostProcessWorker && Boolean(this._extensionHostProcessWorker.getInspectPort()); } @@ -238,3 +300,68 @@ class RPCLogger implements IRPCProtocolLogger { this._log('Win \u2192 Ext', this._totalOutgoing, msgLength, req, initiator, str, data); } } + +interface ExtHostLatencyResult { + remoteAuthority: string; + up: number; + down: number; + latency: number; +} + +interface ExtHostLatencyProvider { + measure(): Promise; +} + +let providers: ExtHostLatencyProvider[] = []; +function registerLatencyTestProvider(provider: ExtHostLatencyProvider): IDisposable { + providers.push(provider); + return { + dispose: () => { + for (let i = 0; i < providers.length; i++) { + if (providers[i] === provider) { + providers.splice(i, 1); + return; + } + } + } + }; +} + +function getLatencyTestProviders(): ExtHostLatencyProvider[] { + return providers.slice(0); +} + +export class MeasureExtHostLatencyAction extends Action { + public static readonly ID = 'editor.action.measureExtHostLatency'; + public static readonly LABEL = nls.localize('measureExtHostLatency', "Developer: Measure Extension Host Latency"); + + constructor( + id: string, + label: string, + @IEditorService private readonly _editorService: IEditorService + ) { + super(id, label); + } + + public async run(): Promise { + const measurements = await Promise.all(getLatencyTestProviders().map(provider => provider.measure())); + this._editorService.openEditor({ contents: measurements.map(MeasureExtHostLatencyAction._print).join('\n\n'), options: { pinned: true } } as IUntitledResourceInput); + } + + private static _print(m: ExtHostLatencyResult): string { + return `${m.remoteAuthority ? `Authority: ${m.remoteAuthority}\n` : ``}Roundtrip latency: ${m.latency.toFixed(3)}ms\nUp: ${MeasureExtHostLatencyAction._printSpeed(m.up)}\nDown: ${MeasureExtHostLatencyAction._printSpeed(m.down)}\n`; + } + + private static _printSpeed(n: number): string { + if (n <= 1024) { + return `${n} bps`; + } + if (n < 1024 * 1024) { + return `${(n / 1024).toFixed(1)} kbps`; + } + return `${(n / 1024 / 1024).toFixed(1)} Mbps`; + } +} + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(MeasureExtHostLatencyAction, MeasureExtHostLatencyAction.ID, MeasureExtHostLatencyAction.LABEL), 'Developer: Measure Extension Host Latency'); From ed504cb36012ac25b57e2964990a5e7676d5ae65 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Jan 2019 09:36:14 +0100 Subject: [PATCH 0093/1066] strict null - snippetController, #60565 --- src/tsconfig.strictNullChecks.json | 3 +- .../contrib/snippet/snippetController2.ts | 6 +-- .../editor/contrib/snippet/snippetSession.ts | 45 +++++++++---------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index bae651862fb..ebf46a30d21 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -241,6 +241,7 @@ "./vs/editor/contrib/referenceSearch/test/referencesModel.test.ts", "./vs/editor/contrib/rename/rename.ts", "./vs/editor/contrib/rename/renameInputField.ts", + "./vs/editor/contrib/snippet/snippetController2.ts", "./vs/editor/contrib/snippet/snippetParser.ts", "./vs/editor/contrib/snippet/snippetVariables.ts", "./vs/editor/contrib/snippet/test/snippetParser.test.ts", @@ -873,4 +874,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/snippet/snippetController2.ts b/src/vs/editor/contrib/snippet/snippetController2.ts index 8db5ba1e17d..febc52c54a6 100644 --- a/src/vs/editor/contrib/snippet/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/snippetController2.ts @@ -150,7 +150,7 @@ export class SnippetController2 implements IEditorContribution { } private _handleChoice(): void { - if (!this._session) { + if (!this._session || !this._editor.hasModel()) { this._currentChoice = undefined; return; } @@ -181,7 +181,7 @@ export class SnippetController2 implements IEditorContribution { // insertText: `\${1|${after.concat(before).join(',')}|}$0`, // snippetType: 'textmate', sortText: repeat('a', i), - range: Range.fromPositions(this._editor.getPosition(), this._editor.getPosition().delta(0, first.value.length)) + range: Range.fromPositions(this._editor.getPosition()!, this._editor.getPosition()!.delta(0, first.value.length)) }; })); } @@ -218,7 +218,7 @@ export class SnippetController2 implements IEditorContribution { } isInSnippet(): boolean { - return this._inSnippet.get(); + return Boolean(this._inSnippet.get()); } getSessionEnclosingRange(): Range | undefined { diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 98c7d98d131..dbb70edcd44 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -7,7 +7,7 @@ import { groupBy } from 'vs/base/common/arrays'; import { dispose } from 'vs/base/common/lifecycle'; import { getLeadingWhitespace } from 'vs/base/common/strings'; import 'vs/css!./snippetSession'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -34,7 +34,7 @@ registerThemingParticipant((theme, collector) => { export class OneSnippet { - private readonly _editor: ICodeEditor; + private readonly _editor: IActiveCodeEditor; private readonly _snippet: TextmateSnippet; private readonly _offset: number; @@ -50,7 +50,7 @@ export class OneSnippet { inactiveFinal: ModelDecorationOptions.register({ stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, className: 'finish-snippet-placeholder' }), }; - constructor(editor: ICodeEditor, snippet: TextmateSnippet, offset: number) { + constructor(editor: IActiveCodeEditor, snippet: TextmateSnippet, offset: number) { this._editor = editor; this._snippet = snippet; this._offset = offset; @@ -109,7 +109,7 @@ export class OneSnippet { // Check if the placeholder has a transformation if (placeholder.transform) { const id = this._placeholderDecorations.get(placeholder); - const range = this._editor.getModel().getDecorationRange(id); + const range = this._editor.getModel().getDecorationRange(id)!; const currentValue = this._editor.getModel().getValueInRange(range); operations.push(EditOperation.replaceMove(range, placeholder.transform.resolve(currentValue))); @@ -146,7 +146,7 @@ export class OneSnippet { const selections: Selection[] = []; for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { const id = this._placeholderDecorations.get(placeholder); - const range = this._editor.getModel().getDecorationRange(id); + const range = this._editor.getModel().getDecorationRange(id)!; selections.push(new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)); // consider to skip this placeholder index when the decoration @@ -173,7 +173,7 @@ export class OneSnippet { }); return selections; - }); + })!; return !skipThisPlaceholder ? newSelections : this.move(fwd); } @@ -222,7 +222,7 @@ export class OneSnippet { return result; } - get choice(): Choice { + get choice(): Choice | undefined { return this._placeholderGroups[this._placeholderGroupsIdx][0].choice; } @@ -238,13 +238,13 @@ export class OneSnippet { // everything is sorted by editor selection we can simply remove // elements from the beginning of the array for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { - const nested = others.shift(); + const nested = others.shift()!; console.assert(!nested._placeholderDecorations); // Massage placeholder-indicies of the nested snippet to be // sorted right after the insertion point. This ensures we move // through the placeholders in the correct order - const indexLastPlaceholder = nested._snippet.placeholderInfo.last.index; + const indexLastPlaceholder = nested._snippet.placeholderInfo.last!.index; for (const nestedPlaceholder of nested._snippet.placeholderInfo.all) { if (nestedPlaceholder.isFinalTabstop) { @@ -280,15 +280,15 @@ export class OneSnippet { }); } - public getEnclosingRange(): Range { + public getEnclosingRange(): Range | undefined { let result: Range | undefined; const model = this._editor.getModel(); this._placeholderDecorations.forEach((decorationId) => { - const placeholderRange = model.getDecorationRange(decorationId); + const placeholderRange = model.getDecorationRange(decorationId) || undefined; if (!result) { result = placeholderRange; } else { - result = result.plusRange(placeholderRange); + result = result.plusRange(placeholderRange!); } }); return result; @@ -343,7 +343,7 @@ export class SnippetSession { return selection; } - static createEditsAndSnippets(editor: ICodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { + static createEditsAndSnippets(editor: IActiveCodeEditor, template: string, overwriteBefore: number, overwriteAfter: number, enforceFinalTabstop: boolean, adjustWhitespace: boolean): { edits: IIdentifiedSingleEditOperation[], snippets: OneSnippet[] } { const edits: IIdentifiedSingleEditOperation[] = []; const snippets: OneSnippet[] = []; @@ -425,7 +425,7 @@ export class SnippetSession { return { edits, snippets }; } - private readonly _editor: ICodeEditor; + private readonly _editor: IActiveCodeEditor; private readonly _template: string; private readonly _templateMerges: [number, number, string][] = []; private readonly _overwriteBefore: number; @@ -433,7 +433,7 @@ export class SnippetSession { private readonly _adjustWhitespace: boolean; private _snippets: OneSnippet[] = []; - constructor(editor: ICodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true) { + constructor(editor: IActiveCodeEditor, template: string, overwriteBefore: number = 0, overwriteAfter: number = 0, adjustWhitespace: boolean = true) { this._editor = editor; this._template = template; this._overwriteBefore = overwriteBefore; @@ -466,7 +466,7 @@ export class SnippetSession { } else { return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); } - }); + })!; this._editor.setSelections(selections); this._editor.revealRange(selections[0]); } @@ -490,7 +490,7 @@ export class SnippetSession { } else { return undoEdits.map(edit => Selection.fromPositions(edit.range.getEndPosition())); } - })); + })!); } next(): void { @@ -526,7 +526,7 @@ export class SnippetSession { return this._snippets[0].hasPlaceholder; } - get choice(): Choice { + get choice(): Choice | undefined { return this._snippets[0].choice; } @@ -544,7 +544,7 @@ export class SnippetSession { return false; } - let allPossibleSelections: Map; + let allPossibleSelections = new Map(); for (const snippet of this._snippets) { const possibleSelections = snippet.computePossibleSelections(); @@ -552,8 +552,7 @@ export class SnippetSession { // for the first snippet find the placeholder (and its ranges) // that contain at least one selection. for all remaining snippets // the same placeholder (and their ranges) must be used. - if (!allPossibleSelections) { - allPossibleSelections = new Map(); + if (allPossibleSelections.size === 0) { possibleSelections.forEach((ranges, index) => { ranges.sort(Range.compareRangesUsingStarts); @@ -607,14 +606,14 @@ export class SnippetSession { return allPossibleSelections.size > 0; } - public getEnclosingRange(): Range { + public getEnclosingRange(): Range | undefined { let result: Range | undefined; for (const snippet of this._snippets) { const snippetRange = snippet.getEnclosingRange(); if (!result) { result = snippetRange; } else { - result = result.plusRange(snippetRange); + result = result.plusRange(snippetRange!); } } return result; From 92244624be22d0b02aaf8dcb506236694270179b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Jan 2019 09:54:54 +0100 Subject: [PATCH 0094/1066] matchesFuzzy2 - allow first match to be weak... --- src/vs/base/common/filters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 03306c56029..f0df6a97776 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -348,7 +348,7 @@ export function matchesFuzzy(word: string, wordToMatchAgainst: string, enableSep * powerfull than `matchesFuzzy` */ export function matchesFuzzy2(pattern: string, word: string): IMatch[] | null { - let score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, false); + let score = fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, true); return score ? createMatches(score) : null; } From 7f0e95dcfc724d04a906274804513dc439259f69 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Jan 2019 09:57:04 +0100 Subject: [PATCH 0095/1066] Fixes #66214: ignore no-op text edits --- .../electron-browser/bulkEditService.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 1337b1f1fb6..83231a821d2 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -69,16 +69,23 @@ class ModelEditTask implements IDisposable { // honor eol-change this._newEol = edit.eol; } - if (edit.range || edit.text) { - // create edit operation - let range: Range; - if (!edit.range) { - range = this._model.getFullModelRange(); - } else { - range = Range.lift(edit.range); - } - this._edits.push(EditOperation.replaceMove(range, edit.text)); + if (!edit.range && !edit.text) { + // lacks both a range and the text + continue; } + if (Range.isEmpty(edit.range) && !edit.text) { + // no-op edit (replace empty range with empty text) + continue; + } + + // create edit operation + let range: Range; + if (!edit.range) { + range = this._model.getFullModelRange(); + } else { + range = Range.lift(edit.range); + } + this._edits.push(EditOperation.replaceMove(range, edit.text)); } } @@ -333,6 +340,7 @@ export class BulkEdit { } private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress): Promise { + console.log(JSON.stringify(edits, null, '\t')); this._logService.debug('_performTextEdits', JSON.stringify(edits)); const recording = Recording.start(this._fileService); From acdfe2bdeb414a059666295e2062bc5075b5c765 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 9 Jan 2019 10:17:26 +0100 Subject: [PATCH 0096/1066] fix npe --- src/vs/base/browser/ui/selectBox/selectBoxNative.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 80f3583bae9..01f59d40fa8 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -115,7 +115,7 @@ export class SelectBoxNative implements ISelectBoxDelegate { } this.selectElement.selectedIndex = this.selected; - if (this.options.length && typeof this.options[this.selected].text === 'string') { + if ((this.selected < this.options.length) && typeof this.options[this.selected].text === 'string') { this.selectElement.title = this.options[this.selected].text; } else { this.selectElement.title = ''; From d01b6c4a86279a127ad5ab0738904380cca53d9a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 9 Jan 2019 10:26:11 +0100 Subject: [PATCH 0097/1066] debug start actions disable when there is already a debug session running and we do not have any launch configuration selected fixes #66047 --- src/vs/workbench/parts/debug/browser/debugActions.ts | 11 ++++++----- .../workbench/parts/debug/browser/debugQuickOpen.ts | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 55ff3660ac6..9a744bd4658 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -156,13 +156,14 @@ export class StartAction extends AbstractDebugAction { return false; } - public static isEnabled(debugService: IDebugService, contextService: IWorkspaceContextService, configName: string) { + public static isEnabled(debugService: IDebugService) { const sessions = debugService.getModel().getSessions(); if (debugService.state === State.Initializing) { return false; } - if (contextService && contextService.getWorkbenchState() === WorkbenchState.EMPTY && sessions.length > 0) { + if ((sessions.length > 0) && debugService.getConfigurationManager().getLaunches().every(l => l.getConfigurationNames().length === 0)) { + // There is already a debug session running and we do not have any launch configuration selected return false; } @@ -170,8 +171,8 @@ export class StartAction extends AbstractDebugAction { } // Disabled if the launch drop down shows the launch config that is already running. - protected isEnabled(state: State): boolean { - return StartAction.isEnabled(this.debugService, this.contextService, this.debugService.getConfigurationManager().selectedConfiguration.name); + protected isEnabled(): boolean { + return StartAction.isEnabled(this.debugService); } } @@ -246,7 +247,7 @@ export class RestartAction extends AbstractDebugAction { return super.isEnabled(state) && ( state === State.Running || state === State.Stopped || - StartAction.isEnabled(this.debugService, this.contextService, this.debugService.getConfigurationManager().selectedConfiguration.name) + StartAction.isEnabled(this.debugService) ); } } diff --git a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts index d32017e208e..3fe73ff856a 100644 --- a/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts +++ b/src/vs/workbench/parts/debug/browser/debugQuickOpen.ts @@ -62,7 +62,7 @@ class StartDebugEntry extends Model.QuickOpenEntry { } public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { - if (mode === QuickOpen.Mode.PREVIEW || !StartAction.isEnabled(this.debugService, this.contextService, this.configurationName)) { + if (mode === QuickOpen.Mode.PREVIEW || !StartAction.isEnabled(this.debugService)) { return false; } // Run selected debug configuration From c71fd468bb62156ea295dd7a3fca37038f65c29a Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 9 Jan 2019 10:37:16 +0100 Subject: [PATCH 0098/1066] debug: set debugtype context key when focused session changes fixes #66226 --- .../workbench/parts/debug/electron-browser/debugService.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index cfebc210a02..b4130cdac63 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -445,7 +445,6 @@ export class DebugService implements IDebugService { } this.viewModel.firstSessionStart = false; - this.debugType.set(session.configuration.type); if (this.model.getSessions().length > 1) { this.viewModel.setMultiSessionView(true); } @@ -535,7 +534,6 @@ export class DebugService implements IDebugService { } if (this.model.getSessions().length === 0) { - this.debugType.reset(); this.viewModel.setMultiSessionView(false); if (this.partService.isVisible(Parts.SIDEBAR_PART) && this.configurationService.getValue('debug').openExplorerOnEnd) { @@ -786,6 +784,11 @@ export class DebugService implements IDebugService { stackFrame.openInEditor(this.editorService, true); aria.alert(nls.localize('debuggingPaused', "Debugging paused {0}, {1} {2}", thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); } + if (session) { + this.debugType.set(session.configuration.type); + } else { + this.debugType.reset(); + } this.viewModel.setFocus(stackFrame, thread, session, explicit); } From 2b2cdae172901707bafbbb99fdd7c945daed085e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 12:16:18 +0100 Subject: [PATCH 0099/1066] list: drag and drop almost working --- src/vs/base/browser/ui/list/list.ts | 10 +- src/vs/base/browser/ui/list/listView.ts | 292 ++++++++++++++++-- src/vs/base/browser/ui/list/listWidget.ts | 182 +++-------- src/vs/base/browser/ui/tree/abstractTree.ts | 6 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 6 +- src/vs/platform/list/browser/listService.ts | 10 +- .../parts/scm/electron-browser/scmViewlet.ts | 5 +- 7 files changed, 330 insertions(+), 181 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 09da2945df9..7933bcda6b9 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -7,7 +7,6 @@ import { GestureEvent } from 'vs/base/browser/touch'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; export interface IListVirtualDelegate { getHeight(element: T): number; @@ -82,6 +81,7 @@ export const enum DragOverEffect { export interface IDragOverReaction { accept: boolean; effect?: DragOverEffect; + feedback?: number[]; // use -1 for entire list // bubble?: DragOverBubble; // autoExpand?: boolean; } @@ -96,16 +96,16 @@ export const DragOverReactions = { }; export interface IDragAndDropData { - update(event: DragMouseEvent): void; + update(dataTransfer: DataTransfer): void; getData(): any; } export interface IDragAndDrop { getDragURI(element: T): string | null; getDragLabel?(elements: T[]): string; - onDragStart(data: IDragAndDropData, originalEvent: DragMouseEvent): void; - onDragOver(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): boolean | IDragOverReaction; - drop(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): void; + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void; + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IDragOverReaction; + drop(data: IDragAndDropData, targetElement: T | undefined, originalEvent: DragEvent): void; } /** diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index d7ec9514290..c815b43f35b 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getOrDefault } from 'vs/base/common/objects'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch'; import * as DOM from 'vs/base/browser/dom'; import { Event, Emitter } from 'vs/base/common/event'; @@ -12,14 +12,15 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IDragAndDrop, IListDragEvent } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IDragAndDrop, IDragAndDropData } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; import { ISpliceable } from 'vs/base/common/sequence'; import { memoize } from 'vs/base/common/decorators'; -import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { Range, IRange } from 'vs/base/common/range'; +import { equals, distinct } from 'vs/base/common/arrays'; +import { DataTransfers } from 'vs/base/browser/dnd'; function canUseTranslate3d(): boolean { if (browser.isFirefox) { @@ -42,11 +43,16 @@ interface IItem { hasDynamicHeight: boolean; renderWidth: number | undefined; uri: string | undefined; + dropTarget: boolean; dragStartDisposable: IDisposable; } +export interface IListViewDragAndDrop extends IDragAndDrop { + getDragElements(element: T): T[]; +} + export interface IListViewOptions { - readonly dnd?: IDragAndDrop; + readonly dnd?: IListViewDragAndDrop; readonly useShadows?: boolean; readonly verticalScrollMode?: ScrollbarVisibility; readonly setRowLineHeight?: boolean; @@ -60,6 +66,7 @@ const DefaultOptions = { setRowLineHeight: true, supportDynamicHeights: false, dnd: { + getDragElements(e) { return [e]; }, getDragURI() { return null; }, onDragStart(): void { }, onDragOver() { return false; }, @@ -67,8 +74,88 @@ const DefaultOptions = { } }; +export class ElementsDragAndDropData implements IDragAndDropData { + + private elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + public update(dataTransfer: DataTransfer): void { + // no-op + } + + public getData(): any { + return this.elements; + } +} + +export class ExternalElementsDragAndDropData implements IDragAndDropData { + + private elements: T[]; + + constructor(elements: T[]) { + this.elements = elements; + } + + public update(dataTransfer: DataTransfer): void { + // no-op + } + + public getData(): any { + return this.elements; + } +} + +export class DesktopDragAndDropData implements IDragAndDropData { + + private types: any[]; + private files: any[]; + + constructor() { + this.types = []; + this.files = []; + } + + public update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { + this.types = [...dataTransfer.types]; + } + + if (dataTransfer.files) { + this.files = []; + + for (let i = 0; i < dataTransfer.files.length; i++) { + const file = dataTransfer.files.item(i); + + if (file && (file.size || file.type)) { + this.files.push(file); + } + } + } + } + + public getData(): any { + return { + types: this.types, + files: this.files + }; + } +} + +function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): boolean { + if (Array.isArray(f1) && Array.isArray(f2)) { + return equals(f1, f2!); + } + + return f1 === f2; +} + export class ListView implements ISpliceable, IDisposable { + private static currentExternalDragAndDropData: IDragAndDropData | undefined; + readonly domNode: HTMLElement; private items: IItem[]; @@ -90,18 +177,26 @@ export class ListView implements ISpliceable, IDisposable { private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; - private readonly dnd: IDragAndDrop; + + private dnd: IListViewDragAndDrop; + private currentDragData: IDragAndDropData | undefined; + private currentDragFeedback: number[] | undefined; + private currentDragFeedbackDisposable: IDisposable = Disposable.None; + private onDragLeaveTimeout: IDisposable = Disposable.None; + private disposables: IDisposable[]; private _onDidChangeContentHeight = new Emitter(); readonly onDidChangeContentHeight: Event = Event.latch(this._onDidChangeContentHeight.event); get contentHeight(): number { return this.rangeMap.size; } - private _onDragStart = new Emitter<{ element: T, uri: string, event: DragMouseEvent }>(); - readonly onDragStart = this._onDragStart.event; + // private _onDragStart = new Emitter<{ element: T, uri: string, event: DragEvent }>(); + // readonly onDragStart = this._onDragStart.event; - readonly onDragOver: Event>; - readonly onDragLeave: Event; + // readonly onDragOver: Event>; + // readonly onDragLeave: Event; + // readonly onDrop: Event>; + // readonly onDragEnd: Event; constructor( container: HTMLElement, @@ -150,18 +245,14 @@ export class ListView implements ISpliceable, IDisposable { domEvent(this.scrollableElement.getDomNode(), 'scroll') (e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables); - const onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => new DragMouseEvent(e)); - onDragOver(this.onDidDragOver, this, this.disposables); - - this.onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e)); - this.onDragLeave = Event.signal(domEvent(this.domNode, 'dragleave')); - - const onDragEnd = Event.map(domEvent(document, 'dragend'), e => new DragMouseEvent(e)); - onDragEnd(this._onDragEnd, this, this.disposables); + Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e))(this.onDragOver, this, this.disposables); + Event.map(domEvent(this.domNode, 'drop'), e => this.toDragEvent(e))(this.onDrop, this, this.disposables); + domEvent(this.domNode, 'dragleave')(this.onDragLeave, this, this.disposables); + domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables); this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); - this.dnd = getOrDefault, IDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); + this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); this.layout(); } @@ -203,6 +294,7 @@ export class ListView implements ISpliceable, IDisposable { renderWidth: undefined, row: null, uri: undefined, + dropTarget: false, dragStartDisposable: Disposable.None })); @@ -385,8 +477,8 @@ export class ListView implements ISpliceable, IDisposable { if (uri) { item.row.domNode!.draggable = true; - const onDragStart = Event.map(domEvent(item.row.domNode!, 'dragstart'), e => new DragMouseEvent(e)); - item.dragStartDisposable = onDragStart(event => this._onDragStart.fire({ element: item.element, uri, event }), this); + const onDragStart = domEvent(item.row.domNode!, 'dragstart'); + item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event)); } } @@ -402,6 +494,7 @@ export class ListView implements ISpliceable, IDisposable { item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false'); item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`); item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`); + DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget); } private removeItemFromDOM(index: number): void { @@ -509,7 +602,154 @@ export class ListView implements ISpliceable, IDisposable { // DND - private onDidDragOver(event: DragMouseEvent): void { + private onDragStart(element: T, uri: string, event: DragEvent): void { + if (!event.dataTransfer) { + return; + } + + const elements = this.dnd.getDragElements(element); + + event.dataTransfer.effectAllowed = 'copyMove'; + event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); + + if (event.dataTransfer.setDragImage) { + let label: string; + + if (this.dnd.getDragLabel) { + label = this.dnd.getDragLabel(elements); + } else { + label = String(elements.length); + } + + const dragImage = DOM.$('.monaco-list-drag-image'); + dragImage.textContent = label; + document.body.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, -10, -10); + setTimeout(() => document.body.removeChild(dragImage), 0); + } + + this.currentDragData = new ElementsDragAndDropData(elements); + ListView.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements); + + this.dnd.onDragStart(this.currentDragData, event); + } + + private onDragOver(event: IListDragEvent): boolean { + console.log('DRAG OVER'); + + this.onDragLeaveTimeout.dispose(); + this.setupDragAndDropScrollTopAnimation(event.browserEvent); + + if (!event.browserEvent.dataTransfer) { + return false; + } + + // Drag over from outside + if (!this.currentDragData) { + if (ListView.currentExternalDragAndDropData) { + // Drag over from another list + this.currentDragData = ListView.currentExternalDragAndDropData; + + } else { + // Drag over from the desktop + if (!event.browserEvent.dataTransfer.types) { + return false; + } + + this.currentDragData = new DesktopDragAndDropData(); + } + } + + const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent); + const canDrop = typeof result === 'boolean' ? result : result.accept; + // const reaction = (typeof result !== 'boolean' && result.effect === DragOverEffect.Copy) ? 'copy' : 'move'; + + if (!canDrop) { + return false; + } + + let feedback: number[]; + + if (typeof result !== 'boolean' && result.feedback) { + feedback = result.feedback; + } else { + if (typeof event.index === 'undefined') { + feedback = [-1]; + } else { + feedback = [event.index]; + } + } + + // sanitize feedback list + feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort(); + feedback = feedback[0] === -1 ? [-1] : feedback; + + if (feedback.length === 0) { + throw new Error('Invalid empty feedback list'); + } + + if (equalsDragFeedback(this.currentDragFeedback, feedback)) { + return true; + } + + console.log(this.currentDragFeedback, feedback); + + this.currentDragFeedback = feedback; + this.currentDragFeedbackDisposable.dispose(); + + if (feedback[0] === -1) { // entire list feedback + DOM.addClass(this.domNode, 'drop-target'); + this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target')); + } else { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = true; + + if (item.row && item.row.domNode) { + DOM.addClass(item.row.domNode, 'drop-target'); + } + } + + this.currentDragFeedbackDisposable = toDisposable(() => { + for (const index of feedback) { + const item = this.items[index]!; + item.dropTarget = false; + + if (item.row && item.row.domNode) { + DOM.removeClass(item.row.domNode, 'drop-target'); + } + } + }); + } + + return true; + } + + private onDragLeave(event: DragEvent): void { + console.log('LEAVE'); + this.onDragLeaveTimeout = DOM.timeout(() => this.clearDragOverFeedback(), 100); + } + + private onDrop(e: IListDragEvent): void { + console.log('DROP'); + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + } + + private onDragEnd(): void { + console.log('DRAG END'); + this.teardownDragAndDropScrollTopAnimation(); + this.clearDragOverFeedback(); + } + + private clearDragOverFeedback(): void { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); + } + + // DND scroll top animation + + private setupDragAndDropScrollTopAnimation(event: DragEvent): void { if (!this.dragOverAnimationDisposable) { const viewTop = DOM.getTopLeftOffset(this.domNode).top; this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop)); @@ -517,11 +757,13 @@ export class ListView implements ISpliceable, IDisposable { this.dragOverAnimationStopDisposable.dispose(); this.dragOverAnimationStopDisposable = DOM.timeout(() => { - this.dragOverAnimationDisposable.dispose(); - this.dragOverAnimationDisposable = undefined; + if (this.dragOverAnimationDisposable) { + this.dragOverAnimationDisposable.dispose(); + this.dragOverAnimationDisposable = undefined; + } }, 1000); - this.dragOverMouseY = event.posy; + this.dragOverMouseY = event.pageY; } private animateDragAndDropScrollTop(viewTop: number): void { @@ -539,7 +781,7 @@ export class ListView implements ISpliceable, IDisposable { } } - private _onDragEnd(): void { + private teardownDragAndDropScrollTopAnimation(): void { this.dragOverAnimationStopDisposable.dispose(); if (this.dragOverAnimationDisposable) { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8e839278ac1..0d11dc51b9e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,17 +16,15 @@ 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, IDragAndDrop, IDragAndDropData, IListDragEvent } from './list'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IDragAndDrop } from './list'; import { ListView, IListViewOptions } from './listView'; import { Color } from 'vs/base/common/color'; -import { mixin, getOrDefault } from 'vs/base/common/objects'; +import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; import { matchesPrefix } from 'vs/base/common/filters'; -import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; -import { DataTransfers } from 'vs/base/browser/dnd'; interface ITraitChangeEvent { indexes: number[]; @@ -214,7 +212,7 @@ class TraitSpliceable implements ISpliceable { splice(start: number, deleteCount: number, elements: T[]): void { if (!this.identityProvider) { - return this.trait.splice(start, deleteCount, elements.map(e => false)); + return this.trait.splice(start, deleteCount, elements.map(() => false)); } const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider!.getId(this.view.element(i)).toString()); @@ -714,13 +712,12 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`); } - // TODO - // if (styles.listDropBackground) { - // content.push(` - // .monaco-tree${suffix} .monaco-tree-wrapper.drop-target, - // .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } - // `); - // } + if (styles.listDropBackground) { + content.push(` + .monaco-list${suffix}.drop-target, + .monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; } + `); + } const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { @@ -729,19 +726,24 @@ export class DefaultStyleController implements IStyleController { } } -export interface IListOptions extends IListViewOptions, IListStyles { - identityProvider?: IIdentityProvider; - dnd?: IDragAndDrop; - keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; - ariaLabel?: string; - mouseSupport?: boolean; - keyboardSupport?: boolean; - verticalScrollMode?: ScrollbarVisibility; - multipleSelectionSupport?: boolean; - multipleSelectionController?: IMultipleSelectionController; - openController?: IOpenController; - styleController?: IStyleController; - accessibilityProvider?: IAccessibilityProvider; +export interface IListOptions extends IListStyles { + readonly identityProvider?: IIdentityProvider; + readonly dnd?: IDragAndDrop; + readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; + readonly ariaLabel?: string; + readonly keyboardSupport?: boolean; + readonly multipleSelectionSupport?: boolean; + readonly multipleSelectionController?: IMultipleSelectionController; + readonly openController?: IOpenController; + readonly styleController?: IStyleController; + readonly accessibilityProvider?: IAccessibilityProvider; + + // list view options + readonly useShadows?: boolean; + readonly verticalScrollMode?: ScrollbarVisibility; + readonly setRowLineHeight?: boolean; + readonly supportDynamicHeights?: boolean; + readonly mouseSupport?: boolean; } export interface IListStyles { @@ -774,7 +776,7 @@ const defaultStyles: IListStyles = { listDropBackground: Color.fromHex('#383B3D') }; -const DefaultOptions: IListOptions = { +const DefaultOptions = { keyboardSupport: true, mouseSupport: true, multipleSelectionSupport: true, @@ -938,73 +940,8 @@ class AccessibiltyRenderer implements IListRenderer { } } -export class ElementsDragAndDropData implements IDragAndDropData { - - private elements: T[]; - - constructor(elements: T[]) { - this.elements = elements; - } - - public update(event: DragMouseEvent): void { - // no-op - } - - public getData(): any { - return this.elements; - } -} - -export class ExternalElementsDragAndDropData implements IDragAndDropData { - - private elements: T[]; - - constructor(elements: T[]) { - this.elements = elements; - } - - public update(event: DragMouseEvent): void { - // no-op - } - - public getData(): any { - return this.elements; - } -} - -export class DesktopDragAndDropData implements IDragAndDropData { - - private types: any[]; - private files: any[]; - - constructor() { - this.types = []; - this.files = []; - } - - public update(event: DragMouseEvent): void { - if (event.dataTransfer.types) { - this.types = [...event.dataTransfer.types]; - } - - if (event.dataTransfer.files) { - this.files = [...event.dataTransfer.files]; - this.files = this.files.filter(f => f.size || f.type); - } - } - - public getData(): any { - return { - types: this.types, - files: this.files - }; - } -} - export class List implements ISpliceable, IDisposable { - private static currentExternalDragAndDropData: IDragAndDropData | undefined; - private static InstanceCount = 0; private idPrefix = `list_id_${++List.InstanceCount}`; @@ -1016,9 +953,6 @@ export class List implements ISpliceable, IDisposable { private styleElement: HTMLStyleElement; private styleController: IStyleController; - private readonly dnd: IDragAndDrop; - private currentDragAndDropData: IDragAndDropData | undefined; - protected disposables: IDisposable[]; @memoize get onFocusChange(): Event> { @@ -1104,7 +1038,6 @@ export class List implements ISpliceable, IDisposable { ) { this.focus = new FocusTrait(i => this.getElementDomId(i)); this.selection = new Trait('selected'); - this.dnd = getOrDefault, IDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); mixin(options, defaultStyles, false); @@ -1116,7 +1049,20 @@ export class List implements ISpliceable, IDisposable { renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); - this.view = new ListView(container, virtualDelegate, renderers, options); + const that = this; + const viewOptions: IListViewOptions = { + ...options, + dnd: options.dnd && { + ...options.dnd, + getDragElements(element) { + const selection = that.getSelectedElements(); + const elements = selection.indexOf(element) > -1 ? selection : [element]; + return elements; + } + } + }; + + this.view = new ListView(container, virtualDelegate, renderers, viewOptions); this.view.domNode.setAttribute('role', 'tree'); DOM.addClass(this.view.domNode, this.idPrefix); this.view.domNode.tabIndex = 0; @@ -1152,11 +1098,6 @@ export class List implements ISpliceable, IDisposable { this.disposables.push(new MouseController(this, this.view, options)); } - this.view.onDragStart(this.onDragStart, this, this.disposables); - - this.view.onDragOver(this.onDragOver, this, this.disposables); - this.view.onDragLeave(this.onDragLeave, this, this.disposables); - this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); @@ -1443,45 +1384,6 @@ export class List implements ISpliceable, IDisposable { DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1); } - // DND - - private onDragStart({ element, uri, event }: { element: T, uri: string, event: DragMouseEvent }): void { - const selection = this.getSelectedElements(); - const elements = selection.indexOf(element) > -1 ? selection : [element]; - - event.dataTransfer.effectAllowed = 'copyMove'; - event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); - - if (event.dataTransfer.setDragImage) { - let label: string; - - if (this.dnd.getDragLabel) { - label = this.dnd.getDragLabel(elements); - } else { - label = String(elements.length); - } - - const dragImage = DOM.$('.monaco-list-drag-image'); - dragImage.textContent = label; - document.body.appendChild(dragImage); - event.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => document.body.removeChild(dragImage), 0); - } - - this.currentDragAndDropData = new ElementsDragAndDropData(elements); - List.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements); - - this.dnd.onDragStart(this.currentDragAndDropData, event); - } - - private onDragOver(event: IListDragEvent): void { - console.log(event); - } - - private onDragLeave(): void { - console.log('LEAVE'); - } - dispose(): void { this._onDidDispose.fire(); this.disposables = dispose(this.disposables); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 0ece88b2d95..494fcef7b0e 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -32,11 +32,11 @@ function asListOptions(options?: IAbstractTreeOptions(options?: IAsyncDataTreeOpt onDragStart(data, originalEvent) { return options.dnd!.onDragStart(data, originalEvent); }, - onDragOver(data, targetNode, originalEvent) { - return options.dnd!.onDragOver(data, targetNode.element as T, originalEvent); + onDragOver(data, targetNode, targetIndex, originalEvent) { + return options.dnd!.onDragOver(data, targetNode && targetNode.element as T, targetIndex, originalEvent); }, drop(data, targetNode, originalEvent) { - return options.dnd!.drop(data, targetNode.element as T, originalEvent); + return options.dnd!.drop(data, targetNode && targetNode.element as T, originalEvent); } }, multipleSelectionController: options.multipleSelectionController && { diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 04f706b9377..d656dfc8e45 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -162,22 +162,24 @@ class WorkbenchOpenController implements IOpenController { } function toWorkbenchListOptions(options: IListOptions, configurationService: IConfigurationService, keybindingService: IKeybindingService): IListOptions { + const result = { ...options }; + if (options.multipleSelectionSupport !== false && !options.multipleSelectionController) { - options.multipleSelectionController = new MultipleSelectionController(configurationService); + result.multipleSelectionController = new MultipleSelectionController(configurationService); } - options.openController = new WorkbenchOpenController(configurationService, options.openController); + result.openController = new WorkbenchOpenController(configurationService, options.openController); if (options.keyboardNavigationLabelProvider) { const tlp = options.keyboardNavigationLabelProvider; - options.keyboardNavigationLabelProvider = { + result.keyboardNavigationLabelProvider = { getKeyboardNavigationLabel(e) { return tlp.getKeyboardNavigationLabel(e); }, mightProducePrintableCharacter(e) { return keybindingService.mightProducePrintableCharacter(e); } }; } - return options; + return result; } let sharedListStyleSheet: HTMLStyleElement; diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 4d619e03f5d..d6b6d3a6bcb 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -878,6 +878,7 @@ export class RepositoryPanel extends ViewletPanel { new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; + const that = this; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { identityProvider: scmResourceIdentityProvider, keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider, @@ -885,7 +886,9 @@ export class RepositoryPanel extends ViewletPanel { getDragURI(element) { return 'file:///foo'; }, // getDragLabel(elements) { return 'dragging'; }, onDragStart(data, originalEvent) { }, - onDragOver(data, targetElement, originalEvent) { return true; }, + onDragOver(data, targetElement, targetIndex, originalEvent) { + return { accept: true, feedback: typeof targetIndex === 'undefined' ? undefined : [targetIndex - 1, targetIndex, targetIndex + 1].filter(i => i >= 0 && i < that.list.length) }; + }, drop(data, targetElement, originalEvent) { } } }) as WorkbenchList; From 0fd082dfef40f40fe209e33c82bdcaa0128b8cc4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 12:20:12 +0100 Subject: [PATCH 0100/1066] :lipstick: --- src/vs/base/browser/ui/list/listWidget.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 0d11dc51b9e..9362f3de6d8 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1049,13 +1049,12 @@ export class List implements ISpliceable, IDisposable { renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); - const that = this; const viewOptions: IListViewOptions = { ...options, dnd: options.dnd && { ...options.dnd, - getDragElements(element) { - const selection = that.getSelectedElements(); + getDragElements: element => { + const selection = this.getSelectedElements(); const elements = selection.indexOf(element) > -1 ? selection : [element]; return elements; } From 2d4511105d36613d7f6fccdcc6fcebc68b7efb48 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 12:50:54 +0100 Subject: [PATCH 0101/1066] list: finish dnd --- src/vs/base/browser/dnd.ts | 15 ++++++- src/vs/base/browser/ui/list/list.ts | 8 +--- src/vs/base/browser/ui/list/listView.ts | 39 +++++++++++-------- src/vs/base/browser/ui/tree/abstractTree.ts | 4 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 +- src/vs/base/parts/tree/browser/tree.ts | 6 +-- .../base/parts/tree/browser/treeDefaults.ts | 7 ++-- src/vs/base/parts/tree/browser/treeDnd.ts | 22 +++++------ src/vs/base/parts/tree/browser/treeView.ts | 18 ++++----- src/vs/workbench/browser/dnd.ts | 4 +- .../electron-browser/views/explorerViewer.ts | 4 +- .../parts/scm/electron-browser/scmViewlet.ts | 12 +----- 12 files changed, 71 insertions(+), 72 deletions(-) diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index a51f6e64b8e..e7703182722 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -83,4 +83,17 @@ export function applyDragImage(event: DragEvent, label: string, clazz: string): // Removes the element when the DND operation is done setTimeout(() => document.body.removeChild(dragImage), 0); } -} \ No newline at end of file +} + +export interface IDragAndDropData { + update(dataTransfer: DataTransfer): void; + getData(): any; +} + +export interface IStaticDND { + CurrentDragAndDropData: IDragAndDropData | undefined; +} + +export const StaticDND: IStaticDND = { + CurrentDragAndDropData: undefined +}; \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 7933bcda6b9..7c314a89ad2 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -7,6 +7,7 @@ import { GestureEvent } from 'vs/base/browser/touch'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface IListVirtualDelegate { getHeight(element: T): number; @@ -95,17 +96,12 @@ export const DragOverReactions = { // acceptCopyBubbleDown(autoExpand = false): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Down, effect: DragOverEffect.Copy, autoExpand }; } }; -export interface IDragAndDropData { - update(dataTransfer: DataTransfer): void; - getData(): any; -} - export interface IDragAndDrop { getDragURI(element: T): string | null; getDragLabel?(elements: T[]): string; onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void; onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IDragOverReaction; - drop(data: IDragAndDropData, targetElement: T | undefined, originalEvent: DragEvent): void; + drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; } /** diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index c815b43f35b..48b8eb36324 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IDragAndDrop, IDragAndDropData } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IDragAndDrop, DragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; @@ -20,7 +20,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; 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 } from 'vs/base/browser/dnd'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; function canUseTranslate3d(): boolean { if (browser.isFirefox) { @@ -154,8 +154,6 @@ function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): export class ListView implements ISpliceable, IDisposable { - private static currentExternalDragAndDropData: IDragAndDropData | undefined; - readonly domNode: HTMLElement; private items: IItem[]; @@ -629,14 +627,12 @@ export class ListView implements ISpliceable, IDisposable { } this.currentDragData = new ElementsDragAndDropData(elements); - ListView.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); this.dnd.onDragStart(this.currentDragData, event); } private onDragOver(event: IListDragEvent): boolean { - console.log('DRAG OVER'); - this.onDragLeaveTimeout.dispose(); this.setupDragAndDropScrollTopAnimation(event.browserEvent); @@ -646,9 +642,9 @@ export class ListView implements ISpliceable, IDisposable { // Drag over from outside if (!this.currentDragData) { - if (ListView.currentExternalDragAndDropData) { + if (StaticDND.CurrentDragAndDropData) { // Drag over from another list - this.currentDragData = ListView.currentExternalDragAndDropData; + this.currentDragData = StaticDND.CurrentDragAndDropData; } else { // Drag over from the desktop @@ -662,12 +658,13 @@ export class ListView implements ISpliceable, IDisposable { const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent); const canDrop = typeof result === 'boolean' ? result : result.accept; - // const reaction = (typeof result !== 'boolean' && result.effect === DragOverEffect.Copy) ? 'copy' : 'move'; if (!canDrop) { return false; } + event.browserEvent.dataTransfer.dropEffect = (typeof result !== 'boolean' && result.effect === DragOverEffect.Copy) ? 'copy' : 'move'; + let feedback: number[]; if (typeof result !== 'boolean' && result.feedback) { @@ -692,8 +689,6 @@ export class ListView implements ISpliceable, IDisposable { return true; } - console.log(this.currentDragFeedback, feedback); - this.currentDragFeedback = feedback; this.currentDragFeedbackDisposable.dispose(); @@ -725,21 +720,31 @@ export class ListView implements ISpliceable, IDisposable { return true; } - private onDragLeave(event: DragEvent): void { - console.log('LEAVE'); + private onDragLeave(): void { this.onDragLeaveTimeout = DOM.timeout(() => this.clearDragOverFeedback(), 100); } - private onDrop(e: IListDragEvent): void { - console.log('DROP'); + private onDrop(event: IListDragEvent): void { + const dragData = this.currentDragData; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; + + if (!dragData || !event.browserEvent.dataTransfer) { + return; + } + + event.browserEvent.preventDefault(); + dragData.update(event.browserEvent.dataTransfer); + this.dnd.drop(dragData, event.element, event.index, event.browserEvent); } private onDragEnd(): void { - console.log('DRAG END'); this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); + this.currentDragData = undefined; + StaticDND.CurrentDragAndDropData = undefined; } private clearDragOverFeedback(): void { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 494fcef7b0e..6e2235e2be6 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -35,8 +35,8 @@ function asListOptions(options?: IAbstractTreeOptions(options?: IAsyncDataTreeOpt onDragOver(data, targetNode, targetIndex, originalEvent) { return options.dnd!.onDragOver(data, targetNode && targetNode.element as T, targetIndex, originalEvent); }, - drop(data, targetNode, originalEvent) { - return options.dnd!.drop(data, targetNode && targetNode.element as T, originalEvent); + drop(data, targetNode, targetIndex, originalEvent) { + return options.dnd!.drop(data, targetNode && targetNode.element as T, targetIndex, originalEvent); } }, multipleSelectionController: options.multipleSelectionController && { diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index 0a933b6c663..1de72e5670b 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -12,6 +12,7 @@ import { Event } from 'vs/base/common/event'; import { IAction, IActionItem } from 'vs/base/common/actions'; import { Color } from 'vs/base/common/color'; import { IItemCollapseEvent, IItemExpandEvent } from 'vs/base/parts/tree/browser/treeModel'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface ITree { @@ -593,11 +594,6 @@ export const DRAG_OVER_ACCEPT_BUBBLE_DOWN = (autoExpand = false) => ({ accept: t export const DRAG_OVER_ACCEPT_BUBBLE_UP_COPY: IDragOverReaction = { accept: true, bubble: DragOverBubble.BUBBLE_UP, effect: DragOverEffect.COPY }; export const DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY = (autoExpand = false) => ({ accept: true, bubble: DragOverBubble.BUBBLE_DOWN, effect: DragOverEffect.COPY, autoExpand }); -export interface IDragAndDropData { - update(event: Mouse.DragMouseEvent): void; - getData(): any; -} - export interface IDragAndDrop { /** diff --git a/src/vs/base/parts/tree/browser/treeDefaults.ts b/src/vs/base/parts/tree/browser/treeDefaults.ts index 03fd5c05780..1b83c5179b8 100644 --- a/src/vs/base/parts/tree/browser/treeDefaults.ts +++ b/src/vs/base/parts/tree/browser/treeDefaults.ts @@ -13,6 +13,7 @@ import * as mouse from 'vs/base/browser/mouseEvent'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as _ from 'vs/base/parts/tree/browser/tree'; import { KeyCode, KeyMod, Keybinding, createKeybinding, SimpleKeybinding, createSimpleKeybinding } from 'vs/base/common/keyCodes'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface IKeyBindingCallback { (tree: _.ITree, event: IKeyboardEvent): void; @@ -434,15 +435,15 @@ export class DefaultDragAndDrop implements _.IDragAndDrop { return null; } - public onDragStart(tree: _.ITree, data: _.IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { + public onDragStart(tree: _.ITree, data: IDragAndDropData, originalEvent: mouse.DragMouseEvent): void { return; } - public onDragOver(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction | null { + public onDragOver(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): _.IDragOverReaction | null { return null; } - public drop(tree: _.ITree, data: _.IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { + public drop(tree: _.ITree, data: IDragAndDropData, targetElement: any, originalEvent: mouse.DragMouseEvent): void { return; } } diff --git a/src/vs/base/parts/tree/browser/treeDnd.ts b/src/vs/base/parts/tree/browser/treeDnd.ts index 5ba6467296b..810e6ce98ee 100644 --- a/src/vs/base/parts/tree/browser/treeDnd.ts +++ b/src/vs/base/parts/tree/browser/treeDnd.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as _ from 'vs/base/parts/tree/browser/tree'; -import * as Mouse from 'vs/base/browser/mouseEvent'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; -export class ElementsDragAndDropData implements _.IDragAndDropData { +export class ElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -14,7 +14,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -23,7 +23,7 @@ export class ElementsDragAndDropData implements _.IDragAndDropData { } } -export class ExternalElementsDragAndDropData implements _.IDragAndDropData { +export class ExternalElementsDragAndDropData implements IDragAndDropData { private elements: any[]; @@ -31,7 +31,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { this.elements = elements; } - public update(event: Mouse.DragMouseEvent): void { + public update(dataTransfer: DataTransfer): void { // no-op } @@ -40,7 +40,7 @@ export class ExternalElementsDragAndDropData implements _.IDragAndDropData { } } -export class DesktopDragAndDropData implements _.IDragAndDropData { +export class DesktopDragAndDropData implements IDragAndDropData { private types: any[]; private files: any[]; @@ -50,15 +50,15 @@ export class DesktopDragAndDropData implements _.IDragAndDropData { this.files = []; } - public update(event: Mouse.DragMouseEvent): void { - if (event.dataTransfer.types) { + public update(dataTransfer: DataTransfer): void { + if (dataTransfer.types) { this.types = []; - Array.prototype.push.apply(this.types, event.dataTransfer.types); + Array.prototype.push.apply(this.types, dataTransfer.types as any); } - if (event.dataTransfer.files) { + if (dataTransfer.files) { this.files = []; - Array.prototype.push.apply(this.files, event.dataTransfer.files); + Array.prototype.push.apply(this.files, dataTransfer.files as any); this.files = this.files.filter(f => f.size || f.type); } diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 144344684ea..c61ec47be8b 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -21,7 +21,7 @@ import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; import * as _ from 'vs/base/parts/tree/browser/tree'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Event, Emitter } from 'vs/base/common/event'; -import { DataTransfers } from 'vs/base/browser/dnd'; +import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { DefaultTreestyler } from './treeDefaults'; import { Delayer, timeout } from 'vs/base/common/async'; @@ -410,8 +410,6 @@ export class TreeView extends HeightMap { private static counter: number = 0; private instance: number; - private static currentExternalDragAndDropData: _.IDragAndDropData = null; - private context: IViewContext; private modelListeners: Lifecycle.IDisposable[]; private model: Model.TreeModel; @@ -438,7 +436,7 @@ export class TreeView extends HeightMap { private isRefreshing = false; private refreshingPreviousChildrenIds: { [id: string]: string[] } = {}; - private currentDragAndDropData: _.IDragAndDropData; + private currentDragAndDropData: IDragAndDropData; private currentDropElement: any; private currentDropElementReaction: _.IDragOverReaction; private currentDropTarget: ViewItem; @@ -1399,7 +1397,7 @@ export class TreeView extends HeightMap { } this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements); - TreeView.currentExternalDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); + StaticDND.CurrentDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); this.context.dnd.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); } @@ -1482,8 +1480,8 @@ export class TreeView extends HeightMap { if (!this.currentDragAndDropData) { // just started dragging - if (TreeView.currentExternalDragAndDropData) { - this.currentDragAndDropData = TreeView.currentExternalDragAndDropData; + if (StaticDND.CurrentDragAndDropData) { + this.currentDragAndDropData = StaticDND.CurrentDragAndDropData; } else { if (!event.dataTransfer.types) { return false; @@ -1493,7 +1491,7 @@ export class TreeView extends HeightMap { } } - this.currentDragAndDropData.update(event); + this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer); let element: any; let item: Model.Item = viewItem.model; @@ -1581,7 +1579,7 @@ export class TreeView extends HeightMap { if (this.currentDropElement) { let event = new Mouse.DragMouseEvent(e); event.preventDefault(); - this.currentDragAndDropData.update(event); + this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer); this.context.dnd.drop(this.context.tree, this.currentDragAndDropData, this.currentDropElement, event); this.onDragEnd(e); } @@ -1598,7 +1596,7 @@ export class TreeView extends HeightMap { this.cancelDragAndDropScrollInterval(); this.currentDragAndDropData = null; - TreeView.currentExternalDragAndDropData = null; + StaticDND.CurrentDragAndDropData = undefined; this.currentDropElement = null; this.currentDropTarget = null; this.dragAndDropMouseY = null; diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index be3ca78e745..bde4319b2d3 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -15,12 +15,12 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { DefaultEndOfLine } from 'vs/editor/common/model'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; -import { DataTransfers } from 'vs/base/browser/dnd'; +import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults'; import { DragMouseEvent } from 'vs/base/browser/mouseEvent'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { MIME_BINARY } from 'vs/base/common/mime'; -import { ITree, IDragAndDropData } from 'vs/base/parts/tree/browser/tree'; +import { ITree } from 'vs/base/parts/tree/browser/tree'; import { isWindows } from 'vs/base/common/platform'; import { coalesce } from 'vs/base/common/arrays'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; 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 fd6eb80e7e6..27f648f793a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -23,7 +23,7 @@ import { IFilesConfiguration, SortOrder } from 'vs/workbench/parts/files/common/ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { FileOperationError, FileOperationResult, IFileService, FileKind } from 'vs/platform/files/common/files'; import { DuplicateFileAction, AddFilesAction, IEditableData, IFileViewletState, FileCopiedContext } from 'vs/workbench/parts/files/electron-browser/fileActions'; -import { IDataSource, ITree, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragAndDropData, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; +import { IDataSource, ITree, IAccessibilityProvider, IRenderer, ContextMenuEvent, ISorter, IFilter, IDragOverReaction, DRAG_OVER_ACCEPT_BUBBLE_DOWN, DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY, DRAG_OVER_ACCEPT_BUBBLE_UP, DRAG_OVER_ACCEPT_BUBBLE_UP_COPY, DRAG_OVER_REJECT } from 'vs/base/parts/tree/browser/tree'; import { DesktopDragAndDropData, ExternalElementsDragAndDropData } from 'vs/base/parts/tree/browser/treeDnd'; import { ClickBehavior } from 'vs/base/parts/tree/browser/treeDefaults'; import { ExplorerItem, NewStatPlaceholder, Model } from 'vs/workbench/parts/files/common/explorerModel'; @@ -46,7 +46,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { extractResources, SimpleFileResourceDragAndDrop, CodeDataTransfers, fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { DataTransfers } from 'vs/base/browser/dnd'; +import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { rtrim } from 'vs/base/common/strings'; diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index d6b6d3a6bcb..19d30856bec 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -878,19 +878,9 @@ export class RepositoryPanel extends ViewletPanel { new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus) ]; - const that = this; this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, { identityProvider: scmResourceIdentityProvider, - keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider, - dnd: { - getDragURI(element) { return 'file:///foo'; }, - // getDragLabel(elements) { return 'dragging'; }, - onDragStart(data, originalEvent) { }, - onDragOver(data, targetElement, targetIndex, originalEvent) { - return { accept: true, feedback: typeof targetIndex === 'undefined' ? undefined : [targetIndex - 1, targetIndex, targetIndex + 1].filter(i => i >= 0 && i < that.list.length) }; - }, - drop(data, targetElement, originalEvent) { } - } + keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider }) as WorkbenchList; Event.chain(this.list.onDidOpen) From cd6769c7a6aec0e1d4c5356787cb9f7583c7e536 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 12:56:09 +0100 Subject: [PATCH 0102/1066] list, tree: rename drag and drop interfaces --- src/vs/base/browser/ui/list/list.ts | 27 ++++++--------------- src/vs/base/browser/ui/list/listView.ts | 6 ++--- src/vs/base/browser/ui/list/listWidget.ts | 4 +-- src/vs/base/browser/ui/tree/abstractTree.ts | 3 ++- src/vs/base/browser/ui/tree/tree.ts | 24 +++++++++++++++++- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 7c314a89ad2..e3e03f491e6 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -69,38 +69,27 @@ export interface IKeyboardNavigationLabelProvider { mightProducePrintableCharacter?(event: IKeyboardEvent): boolean; } -export const enum DragOverEffect { +export const enum ListDragOverEffect { Copy, Move } -// export const enum DragOverBubble { -// Down, -// Up -// } - -export interface IDragOverReaction { +export interface IListDragOverReaction { accept: boolean; - effect?: DragOverEffect; + effect?: ListDragOverEffect; feedback?: number[]; // use -1 for entire list - // bubble?: DragOverBubble; - // autoExpand?: boolean; } -export const DragOverReactions = { - reject(): IDragOverReaction { return { accept: false }; }, - accept(): IDragOverReaction { return { accept: true }; }, - // acceptBubbleUp(): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Up }; }, - // acceptBubbleDown(autoExpand = false): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Down, autoExpand }; }, - // acceptCopyBubbleUp(): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Up, effect: DragOverEffect.Copy }; }, - // acceptCopyBubbleDown(autoExpand = false): IDragOverReaction { return { accept: true, bubble: DragOverBubble.Down, effect: DragOverEffect.Copy, autoExpand }; } +export const ListDragOverReactions = { + reject(): IListDragOverReaction { return { accept: false }; }, + accept(): IListDragOverReaction { return { accept: true }; }, }; -export interface IDragAndDrop { +export interface IListDragAndDrop { getDragURI(element: T): string | null; getDragLabel?(elements: T[]): string; onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void; - onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IDragOverReaction; + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 48b8eb36324..43a909fba72 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -12,7 +12,7 @@ import { domEvent } from 'vs/base/browser/event'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable'; import { RangeMap, shift } from './rangeMap'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IDragAndDrop, DragOverEffect } from './list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IListDragAndDrop, ListDragOverEffect } from './list'; import { RowCache, IRow } from './rowCache'; import { isWindows } from 'vs/base/common/platform'; import * as browser from 'vs/base/browser/browser'; @@ -47,7 +47,7 @@ interface IItem { dragStartDisposable: IDisposable; } -export interface IListViewDragAndDrop extends IDragAndDrop { +export interface IListViewDragAndDrop extends IListDragAndDrop { getDragElements(element: T): T[]; } @@ -663,7 +663,7 @@ export class ListView implements ISpliceable, IDisposable { return false; } - event.browserEvent.dataTransfer.dropEffect = (typeof result !== 'boolean' && result.effect === DragOverEffect.Copy) ? 'copy' : 'move'; + event.browserEvent.dataTransfer.dropEffect = (typeof result !== 'boolean' && result.effect === ListDragOverEffect.Copy) ? 'copy' : 'move'; let feedback: number[]; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 9362f3de6d8..bd4b892b241 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, IDragAndDrop } from './list'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop } from './list'; import { ListView, IListViewOptions } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -728,7 +728,7 @@ export class DefaultStyleController implements IStyleController { export interface IListOptions extends IListStyles { readonly identityProvider?: IIdentityProvider; - readonly dnd?: IDragAndDrop; + readonly dnd?: IListDragAndDrop; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly ariaLabel?: string; readonly keyboardSupport?: boolean; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 6e2235e2be6..d42dd59eae1 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -11,7 +11,7 @@ import { append, $, toggleClass } from 'vs/base/browser/dom'; import { Event, Relay } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; function asListOptions(options?: IAbstractTreeOptions): IListOptions> | undefined { @@ -198,6 +198,7 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IListOptions { collapseByDefault?: boolean; // defaults to false filter?: ITreeFilter; + readonly dnd?: ITreeDragAndDrop; } export abstract class AbstractTree implements IDisposable { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index baa51afa25d..6d1fcafb9ab 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -5,7 +5,8 @@ import { Event } from 'vs/base/common/event'; import { Iterator } from 'vs/base/common/iterator'; -import { IListRenderer, AbstractListRenderer } from 'vs/base/browser/ui/list/list'; +import { IListRenderer, AbstractListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; export const enum TreeVisibility { @@ -154,6 +155,27 @@ export interface IAsyncDataSource { getChildren(element: TInput | T): T[] | Promise; } +export const enum TreeDragOverBubble { + Down, + Up +} + +export interface ITreeDragOverReaction extends IListDragOverReaction { + bubble?: TreeDragOverBubble; + autoExpand?: boolean; +} + +export const TreeDragOverReactions = { + acceptBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up }; }, + acceptBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand }; }, + acceptCopyBubbleUp(): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Up, effect: ListDragOverEffect.Copy }; }, + acceptCopyBubbleDown(autoExpand = false): ITreeDragOverReaction { return { accept: true, bubble: TreeDragOverBubble.Down, effect: ListDragOverEffect.Copy, autoExpand }; } +}; + +export interface ITreeDragAndDrop extends IListDragAndDrop { + onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction; +} + /** * Use this renderer when you want to re-render elements on account of * an event firing. From 84e9d102fd04f4054dbb6a01d7a779b9027cc9e8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Jan 2019 14:41:39 +0100 Subject: [PATCH 0103/1066] Define the ISuggestMemoryService in the standalone editor --- src/vs/editor/contrib/suggest/suggestMemory.ts | 2 +- src/vs/editor/standalone/browser/standaloneServices.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestMemory.ts b/src/vs/editor/contrib/suggest/suggestMemory.ts index 0c465dac732..f855098734b 100644 --- a/src/vs/editor/contrib/suggest/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/suggestMemory.ts @@ -197,7 +197,7 @@ export class PrefixMemory extends Memory { export type MemMode = 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'; -class SuggestMemoryService extends Disposable implements ISuggestMemoryService { +export class SuggestMemoryService extends Disposable implements ISuggestMemoryService { readonly _serviceBrand: any; diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index 0255ecc1d94..60ccd6786c2 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -44,6 +44,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { MenuService } from 'vs/platform/actions/common/menuService'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; +import { ISuggestMemoryService, SuggestMemoryService } from 'vs/editor/contrib/suggest/suggestMemory'; export interface IEditorOverrideServices { [index: string]: any; @@ -151,6 +152,8 @@ export module StaticServices { export const logService = define(ILogService, () => new NullLogService()); + export const suggestMemoryService = define(ISuggestMemoryService, (o) => new SuggestMemoryService(storageService.get(o), configurationService.get(o))); + } export class DynamicStandaloneServices extends Disposable { From 948c3768d637ae0fb312438d99235f0d95bd7dcd Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Jan 2019 15:07:21 +0100 Subject: [PATCH 0104/1066] Fixes Microsoft/monaco-editor#1235: Evaluate rules on empty lines --- .../standalone/common/monarch/monarchLexer.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index 40954d858a7..0deb0ed169a 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -531,7 +531,12 @@ class MonarchTokenizer implements modes.ITokenizationSupport { } let groupMatching: GroupMatching | null = null; - while (pos < lineLength) { + // See https://github.com/Microsoft/monaco-editor/issues/1235: + // Evaluate rules at least once for an empty line + let forceEvaluation = true; + + while (forceEvaluation || pos < lineLength) { + const pos0 = pos; const stackLen0 = stack.depth; const groupLen0 = groupMatching ? groupMatching.groups.length : 0; @@ -559,11 +564,13 @@ class MonarchTokenizer implements modes.ITokenizationSupport { } else { // otherwise we match on the token stream - if (pos >= lineLength) { + if (!forceEvaluation && pos >= lineLength) { // nothing to do break; } + forceEvaluation = false; + // get the rules for this state let rules: monarchCommon.IRule[] | null = this._lexer.tokenizer[state]; if (!rules) { @@ -606,7 +613,7 @@ class MonarchTokenizer implements modes.ITokenizationSupport { action = this._lexer.defaultToken; } - if (!matched) { + if (matched === null) { // should never happen, needed for strict null checking break; } @@ -749,11 +756,10 @@ class MonarchTokenizer implements modes.ITokenizationSupport { // check progress if (matched.length === 0) { - if (stackLen0 !== stack.depth || state !== stack.state || (!groupMatching ? 0 : groupMatching.groups.length) !== groupLen0) { + if (lineLength === 0 || stackLen0 !== stack.depth || state !== stack.state || (!groupMatching ? 0 : groupMatching.groups.length) !== groupLen0) { continue; } else { throw monarchCommon.createError(this._lexer, 'no progress in tokenizer in rule: ' + this._safeRuleName(rule)); - pos = lineLength; // must make progress or editor loops } } @@ -765,7 +771,6 @@ class MonarchTokenizer implements modes.ITokenizationSupport { let bracket = findBracket(this._lexer, matched); if (!bracket) { throw monarchCommon.createError(this._lexer, '@brackets token returned but no bracket defined as: ' + matched); - bracket = { token: '', bracketType: monarchCommon.MonarchBracket.None }; } tokenType = monarchCommon.sanitize(bracket.token + rest); } else { From d09831327127e9ed0e870cd5a701c20164cbab9c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 9 Jan 2019 15:14:49 +0100 Subject: [PATCH 0105/1066] Remove left-over console.log --- .../services/bulkEdit/electron-browser/bulkEditService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index 83231a821d2..d570f8712de 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -340,7 +340,6 @@ export class BulkEdit { } private async _performTextEdits(edits: ResourceTextEdit[], progress: IProgress): Promise { - console.log(JSON.stringify(edits, null, '\t')); this._logService.debug('_performTextEdits', JSON.stringify(edits)); const recording = Recording.start(this._fileService); From 652813798ad0bcafb8b1bf26ea96e6d727b9bb86 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 9 Jan 2019 15:18:13 +0100 Subject: [PATCH 0106/1066] extensions with debuggers defaults to workspace extension --- src/vs/platform/extensions/common/extensions.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 69fb205d583..78ee711a9ae 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -7,6 +7,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import * as strings from 'vs/base/common/strings'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; export const USER_MANIFEST_CACHE_FILE = 'user'; @@ -29,7 +30,19 @@ export function isUIExtension(manifest: IExtensionManifest, configurationService switch (manifest.extensionKind) { case 'ui': return true; case 'workspace': return false; - default: return uiExtensions.has(extensionId) || !manifest.main; + 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; + } } } From 7b50be29087fdcaf9d3bddb26c9b312f5347b1bc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 16:40:25 +0100 Subject: [PATCH 0107/1066] tree: dnd bubble behavior --- src/vs/base/browser/ui/list/list.ts | 2 +- src/vs/base/browser/ui/list/listView.ts | 8 +- src/vs/base/browser/ui/list/listWidget.ts | 45 +++++++--- src/vs/base/browser/ui/tree/abstractTree.ts | 82 ++++++++++++++----- src/vs/base/browser/ui/tree/indexTreeModel.ts | 4 + .../base/browser/ui/tree/objectTreeModel.ts | 5 ++ src/vs/base/browser/ui/tree/tree.ts | 1 + 7 files changed, 113 insertions(+), 34 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index e3e03f491e6..074d10944de 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -87,7 +87,7 @@ export const ListDragOverReactions = { export interface IListDragAndDrop { getDragURI(element: T): string | null; - getDragLabel?(elements: T[]): string; + getDragLabel?(elements: T[]): string | undefined; onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void; onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 43a909fba72..aa98aae01d0 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -611,11 +611,13 @@ export class ListView implements ISpliceable, IDisposable { event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri])); if (event.dataTransfer.setDragImage) { - let label: string; + let label: string | undefined; if (this.dnd.getDragLabel) { label = this.dnd.getDragLabel(elements); - } else { + } + + if (typeof label === 'undefined') { label = String(elements.length); } @@ -721,6 +723,7 @@ export class ListView implements ISpliceable, IDisposable { } private onDragLeave(): void { + this.onDragLeaveTimeout.dispose(); this.onDragLeaveTimeout = DOM.timeout(() => this.clearDragOverFeedback(), 100); } @@ -750,6 +753,7 @@ export class ListView implements ISpliceable, IDisposable { private clearDragOverFeedback(): void { this.currentDragFeedback = undefined; this.currentDragFeedbackDisposable.dispose(); + this.currentDragFeedbackDisposable = Disposable.None; } // DND scroll top animation diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index bd4b892b241..bb91459c2a2 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -16,8 +16,8 @@ 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 } from './list'; -import { ListView, IListViewOptions } from './listView'; +import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IListDragAndDrop, IListDragOverReaction } from './list'; +import { ListView, IListViewOptions, IListViewDragAndDrop } from './listView'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; @@ -25,6 +25,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice'; import { clamp } from 'vs/base/common/numbers'; import { matchesPrefix } from 'vs/base/common/filters'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; interface ITraitChangeEvent { indexes: number[]; @@ -940,6 +941,37 @@ class AccessibiltyRenderer implements IListRenderer { } } +class ListViewDragAndDrop implements IListViewDragAndDrop { + + constructor(private list: List, private dnd: IListDragAndDrop) { } + + getDragElements(element: T): T[] { + const selection = this.list.getSelectedElements(); + const elements = selection.indexOf(element) > -1 ? selection : [element]; + return elements; + } + + getDragURI(element: T): string | null { + return this.dnd.getDragURI(element); + } + + getDragLabel?(elements: T[]): string | undefined { + return this.dnd.getDragLabel && this.dnd.getDragLabel(elements); + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + this.dnd.onDragStart(data, originalEvent); + } + + onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { + return this.dnd.onDragOver(data, targetElement, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): void { + this.dnd.drop(data, targetElement, targetIndex, originalEvent); + } +} + export class List implements ISpliceable, IDisposable { private static InstanceCount = 0; @@ -1051,14 +1083,7 @@ export class List implements ISpliceable, IDisposable { const viewOptions: IListViewOptions = { ...options, - dnd: options.dnd && { - ...options.dnd, - getDragElements: element => { - const selection = this.getSelectedElements(); - const elements = selection.indexOf(element) > -1 ? selection : [element]; - return elements; - } - } + dnd: options.dnd && new ListViewDragAndDrop(this, options.dnd) }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d42dd59eae1..1f4ff358c3d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -6,15 +6,71 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass } from 'vs/base/browser/dom'; import { Event, Relay } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { range } from 'vs/base/common/arrays'; -function asListOptions(options?: IAbstractTreeOptions): IListOptions> | undefined { +class TreeNodeListDragAndDrop implements IListDragAndDrop> { + + constructor(private modelProvider: () => ITreeModel, private dnd: ITreeDragAndDrop) { } + + getDragURI(node: ITreeNode): string | null { + return this.dnd.getDragURI(node.element); + } + + getDragLabel(nodes: ITreeNode[]): string | undefined { + return this.dnd.getDragLabel && this.dnd.getDragLabel(nodes.map(node => node.element)); + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + this.dnd.onDragStart(data, originalEvent); + } + + onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, force = false): boolean | IListDragOverReaction { + const result = this.dnd.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); + + if (typeof targetNode === 'undefined') { + return result; + } + + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { + if (force) { + const accept = typeof result === 'boolean' ? result : result.accept; + const effect = typeof result === 'boolean' ? undefined : result.effect; + return { accept, effect, feedback: [targetIndex!] }; + } + + return result; + } + + if (result.bubble === TreeDragOverBubble.Up) { + const parentNode = targetNode.parent; + const model = this.modelProvider(); + const parentIndex = parentNode && model.getListIndex(model.getNodeLocation(parentNode)); + + return this.onDragOver(data, parentNode, parentIndex, originalEvent, true); + } + + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + const start = model.getListIndex(ref); + const length = model.getListRenderCount(ref); + + return { ...result, feedback: range(start, start + length) }; + } + + drop(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.dnd.drop(data, targetNode && targetNode.element, targetIndex, originalEvent); + } +} + +function asListOptions(modelProvider: () => ITreeModel, options?: IAbstractTreeOptions): IListOptions> | undefined { return options && { ...options, identityProvider: options.identityProvider && { @@ -22,23 +78,7 @@ function asListOptions(options?: IAbstractTreeOptions { - return options.dnd!.getDragLabel!(nodes.map(node => node.element)); - }), - onDragStart(data, originalEvent) { - return options.dnd!.onDragStart(data, originalEvent); - }, - onDragOver(data, targetNode, targetIndex, originalEvent) { - return options.dnd!.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); - }, - drop(data, targetNode, targetIndex, originalEvent) { - return options.dnd!.drop(data, targetNode && targetNode.element, targetIndex, originalEvent); - } - }, + dnd: options.dnd && new TreeNodeListDragAndDrop(modelProvider, options.dnd), multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); @@ -239,7 +279,7 @@ export abstract class AbstractTree implements IDisposable const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event)); this.disposables.push(...treeRenderers); - this.view = new List(container, treeDelegate, treeRenderers, asListOptions(options)); + this.view = new List(container, treeDelegate, treeRenderers, asListOptions(() => this.model, options)); this.model = this.createModel(this.view, options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 3df1cdee8e8..fe9aa374c78 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -127,6 +127,10 @@ export class IndexTreeModel, TFilterData = voi return this.getTreeNodeWithListIndex(location).listIndex; } + getListRenderCount(location: number[]): number { + return this.getTreeNode(location).renderNodeCount; + } + isCollapsible(location: number[]): boolean { return this.getTreeNode(location).collapsible; } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index c8cc6dbc253..7ed2449e061 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -123,6 +123,11 @@ export class ObjectTreeModel, TFilterData extends Non return this.model.getListIndex(location); } + getListRenderCount(element: T): number { + const location = this.getElementLocation(element); + return this.model.getListRenderCount(location); + } + isCollapsible(element: T): boolean { const location = this.getElementLocation(element); return this.model.isCollapsible(location); diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 6d1fcafb9ab..10078590931 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -101,6 +101,7 @@ export interface ITreeModel { readonly onDidChangeRenderNodeCount: Event>; getListIndex(location: TRef): number; + getListRenderCount(location: TRef): number; getNode(location?: TRef): ITreeNode; getNodeLocation(node: ITreeNode): TRef; getParentNodeLocation(location: TRef): TRef; From 742e27c6ba25e2b58034701390b44a0c7e3399ee Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 16:49:19 +0100 Subject: [PATCH 0108/1066] tree: dnd auto expand --- src/vs/base/browser/ui/tree/abstractTree.ts | 31 +++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1f4ff358c3d..90dc2031d9b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass } from 'vs/base/browser/dom'; +import { append, $, toggleClass, timeout } from 'vs/base/browser/dom'; import { Event, Relay } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -18,6 +18,9 @@ import { range } from 'vs/base/common/arrays'; class TreeNodeListDragAndDrop implements IListDragAndDrop> { + private autoExpandNode: ITreeNode | undefined; + private autoExpandDisposable: IDisposable = Disposable.None; + constructor(private modelProvider: () => ITreeModel, private dnd: ITreeDragAndDrop) { } getDragURI(node: ITreeNode): string | null { @@ -32,15 +35,33 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< this.dnd.onDragStart(data, originalEvent); } - onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, force = false): boolean | IListDragOverReaction { + onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { const result = this.dnd.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); if (typeof targetNode === 'undefined') { return result; } + if (this.autoExpandNode !== targetNode) { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = targetNode; + } + + if (typeof result !== 'boolean' && result.autoExpand) { + this.autoExpandDisposable = timeout(() => { + const model = this.modelProvider(); + const ref = model.getNodeLocation(targetNode); + + if (model.isCollapsed(ref)) { + model.setCollapsed(ref, false); + } + + this.autoExpandNode = undefined; + }, 500); + } + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { - if (force) { + if (!raw) { const accept = typeof result === 'boolean' ? result : result.accept; const effect = typeof result === 'boolean' ? undefined : result.effect; return { accept, effect, feedback: [targetIndex!] }; @@ -54,7 +75,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< const model = this.modelProvider(); const parentIndex = parentNode && model.getListIndex(model.getNodeLocation(parentNode)); - return this.onDragOver(data, parentNode, parentIndex, originalEvent, true); + return this.onDragOver(data, parentNode, parentIndex, originalEvent, false); } const model = this.modelProvider(); From 39e24608edc8d937af6560978eff2b9693e1f445 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 9 Jan 2019 16:52:35 +0100 Subject: [PATCH 0109/1066] tree: fix dnd auto expand timeout --- src/vs/base/browser/ui/tree/abstractTree.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 90dc2031d9b..b91ee43373d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -37,17 +37,18 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { const result = this.dnd.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); + const didChangeAutoExpandNode = this.autoExpandNode !== targetNode; + + if (didChangeAutoExpandNode) { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = targetNode; + } if (typeof targetNode === 'undefined') { return result; } - if (this.autoExpandNode !== targetNode) { - this.autoExpandDisposable.dispose(); - this.autoExpandNode = targetNode; - } - - if (typeof result !== 'boolean' && result.autoExpand) { + if (didChangeAutoExpandNode && typeof result !== 'boolean' && result.autoExpand) { this.autoExpandDisposable = timeout(() => { const model = this.modelProvider(); const ref = model.getNodeLocation(targetNode); From 29f555c6fb7563ba64a0df29a58117ac1240685d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 9 Jan 2019 17:37:28 +0100 Subject: [PATCH 0110/1066] debt - simplify perf util --- src/vs/base/common/performance.d.ts | 10 +- src/vs/base/common/performance.js | 101 +++++------------- .../electron-browser/perfviewEditor.ts | 2 +- 3 files changed, 29 insertions(+), 84 deletions(-) diff --git a/src/vs/base/common/performance.d.ts b/src/vs/base/common/performance.d.ts index dfdb1251598..657f250d8b3 100644 --- a/src/vs/base/common/performance.d.ts +++ b/src/vs/base/common/performance.d.ts @@ -4,22 +4,18 @@ *--------------------------------------------------------------------------------------------*/ export interface PerformanceEntry { - readonly type: 'mark' | 'measure'; readonly name: string; - readonly startTime: number; - readonly duration: number; + readonly timestamp: number; } export function mark(name: string): void; -export function measure(name: string, from?: string, to?: string): PerformanceEntry; - /** * All entries filtered by type and sorted by `startTime`. */ -export function getEntries(type: 'mark' | 'measure'): PerformanceEntry[]; +export function getEntries(): PerformanceEntry[]; -export function getEntry(type: 'mark' | 'measure', name: string): PerformanceEntry; +export function getEntry(name: string): PerformanceEntry; export function getDuration(from: string, to: string): number; diff --git a/src/vs/base/common/performance.js b/src/vs/base/common/performance.js index 14c5a33e72e..c7825a0e310 100644 --- a/src/vs/base/common/performance.js +++ b/src/vs/base/common/performance.js @@ -10,11 +10,11 @@ // This module can be loaded in an amd and commonjs-context. // Because we want both instances to use the same perf-data // we store them globally -// stores data as 'type','name','startTime','duration' +// stores data as: 'name','timestamp' if (typeof define !== "function" && typeof module === "object" && typeof module.exports === "object") { // this is commonjs, fake amd - global.define = function (dep, callback) { + global.define = function (_dep, callback) { module.exports = callback(); global.define = undefined; }; @@ -22,14 +22,10 @@ if (typeof define !== "function" && typeof module === "object" && typeof module. define([], function () { - var _global = this; - if (typeof global !== 'undefined') { - _global = global; - } - _global._performanceEntries = _global._performanceEntries || []; + global._performanceEntries = global._performanceEntries || []; - // const _now = global.performance && performance.now ? performance.now : Date.now - const _now = Date.now; + const _dataLen = 2; + const _timeStamp = typeof console.timeStamp === 'function' ? console.timeStamp.bind(console) : () => { }; function importEntries(entries) { global._performanceEntries.splice(0, 0, ...entries); @@ -39,36 +35,25 @@ define([], function () { return global._performanceEntries.slice(0); } - function getEntries(type, name) { + function getEntries() { const result = []; const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 5) { - if (entries[i] === type && (name === undefined || entries[i + 1] === name)) { - result.push({ - type: entries[i], - name: entries[i + 1], - startTime: entries[i + 2], - duration: entries[i + 3], - seq: entries[i + 4], - }); - } + for (let i = 0; i < entries.length; i += _dataLen) { + result.push({ + name: entries[i], + timestamp: entries[i + 1], + }); } - - return result.sort((a, b) => { - return a.startTime - b.startTime || a.seq - b.seq; - }); + return result; } - function getEntry(type, name) { + function getEntry(name) { const entries = global._performanceEntries; - for (let i = 0; i < entries.length; i += 5) { - if (entries[i] === type && entries[i + 1] === name) { + for (let i = 0; i < entries.length; i += _dataLen) { + if (entries[i] === name) { return { - type: entries[i], - name: entries[i + 1], - startTime: entries[i + 2], - duration: entries[i + 3], - seq: entries[i + 4], + name: entries[i], + timestamp: entries[i + 1], }; } } @@ -77,65 +62,29 @@ define([], function () { function getDuration(from, to) { const entries = global._performanceEntries; let target = to; - let endTime = 0; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === target) { + let endIndex = 0; + for (let i = entries.length - _dataLen; i >= 0; i -= _dataLen) { + if (entries[i] === target) { if (target === to) { // found `to` (end of interval) - endTime = entries[i - 2]; + endIndex = i; target = from; } else { - return endTime - entries[i - 2]; + // found `from` (start of interval) + return entries[endIndex + 1] - entries[i + 1]; } } } return 0; } - let seq = 0; - function mark(name) { - global._performanceEntries.push('mark', name, _now(), 0, seq++); - if (typeof console.timeStamp === 'function') { - console.timeStamp(name); - } - } - - function measure(name, from, to) { - - let startTime; - let duration; - let now = _now(); - - if (!from) { - startTime = now; - } else { - startTime = _getLastStartTime(from); - } - - if (!to) { - duration = now - startTime; - } else { - duration = _getLastStartTime(to) - startTime; - } - - global._performanceEntries.push('measure', name, startTime, duration); - } - - function _getLastStartTime(name) { - const entries = global._performanceEntries; - for (let i = entries.length - 1; i >= 0; i -= 5) { - if (entries[i - 3] === name) { - return entries[i - 2]; - } - } - - throw new Error(name + ' not found'); + global._performanceEntries.push(name, Date.now()); + _timeStamp(name); } var exports = { mark: mark, - measure: measure, getEntries: getEntries, getEntry: getEntry, getDuration: getDuration, diff --git a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts index f79cdf3f515..bfef17ef8ce 100644 --- a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts @@ -194,7 +194,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { md.value += `Name\tTimestamp\tDelta\tTotal\n`; let lastStartTime = -1; let total = 0; - for (const { name, startTime } of perf.getEntries('mark')) { + for (const { name, timestamp: startTime } of perf.getEntries()) { let delta = lastStartTime !== -1 ? startTime - lastStartTime : 0; total += delta; md.value += `${name}\t${startTime}\t${delta}\t${total}\n`; From 08e36c75c85d9fb9f386e2973673307b51852394 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 9 Jan 2019 17:59:49 +0100 Subject: [PATCH 0111/1066] watch expression adopt dnd --- .../electron-browser/watchExpressionsView.ts | 81 ++++++++++--------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 81cee63a177..a921708e0a7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -26,7 +26,9 @@ import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/parts/debug/ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { onUnexpectedError } from 'vs/base/common/errors'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; @@ -67,11 +69,11 @@ export class WatchExpressionsView extends ViewletPanel { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), accessibilityProvider: new WatchExpressionsAccessibilityProvider(), identityProvider: { getId: element => element.getId() }, - keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e } + keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e }, + dnd: new WatchExpressionsDragAndDrop(this.debugService), }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); - // TODO@isidor this is a promise - this.tree.setInput(this.debugService); + this.tree.setInput(this.debugService).then(undefined, onUnexpectedError); const addWatchExpressionAction = new AddWatchExpressionAction(AddWatchExpressionAction.ID, AddWatchExpressionAction.LABEL, this.debugService, this.keybindingService); const collapseAction = new CollapseAction2(this.tree, true, 'explorer-action collapse-explorer'); @@ -257,47 +259,46 @@ class WatchExpressionsAccessibilityProvider implements IAccessibilityProvider { -// constructor(private debugService: IDebugService) { -// super(); -// } + constructor(private debugService: IDebugService) { } -// getDragURI(tree: ITree, element: Expression): string { -// if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()) { -// return null; -// } + onDragOver(data: IDragAndDropData, targetElement: IExpression, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + const draggedData = data.getData(); + if (Array.isArray(draggedData) && draggedData[0] && draggedData[0].element.element instanceof Expression) { + return true; + } -// return element.getId(); -// } + return false; + } -// getDragLabel(tree: ITree, elements: Expression[]): string { -// if (elements.length > 1) { -// return String(elements.length); -// } + getDragURI(element: IExpression): string { + if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()) { + return null; + } -// return elements[0].name; -// } + return element.getId(); + } -// onDragOver(tree: ITree, data: IDragAndDropData, target: Expression | DebugModel, originalEvent: DragMouseEvent): IDragOverReaction { -// if (target instanceof Expression || target instanceof DebugModel) { -// return { -// accept: true, -// autoExpand: false -// }; -// } + getDragLabel?(elements: IExpression[]): string { + if (elements.length > 1) { + return String(elements.length); + } -// return DRAG_OVER_REJECT; -// } + return elements[0].name; + } -// drop(tree: ITree, data: IDragAndDropData, target: Expression | DebugModel, originalEvent: DragMouseEvent): void { -// const draggedData = data.getData(); -// if (Array.isArray(draggedData)) { -// const draggedElement = draggedData[0]; -// const watches = this.debugService.getModel().getWatchExpressions(); -// const position = target instanceof DebugModel ? watches.length - 1 : watches.indexOf(target); -// this.debugService.moveWatchExpression(draggedElement.getId(), position); -// } -// } -// } + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + // noop + } + + drop(data: IDragAndDropData, targetElement: IExpression, targetIndex: number, originalEvent: DragEvent): void { + const draggedData = data.getData(); + if (Array.isArray(draggedData)) { + const draggedElement = draggedData[0].element.element; + const watches = this.debugService.getModel().getWatchExpressions(); + const position = targetElement instanceof Expression ? watches.indexOf(targetElement) : watches.length - 1; + this.debugService.moveWatchExpression(draggedElement.getId(), position); + } + } +} From 2285f7234ef5be7f3c7190c5306404bfc397ab02 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Jan 2019 18:08:26 +0100 Subject: [PATCH 0112/1066] fix compilation --- .../parts/debug/electron-browser/watchExpressionsView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index a921708e0a7..ba2c6babe7a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -26,7 +26,7 @@ import { VariablesRenderer, variableSetEmitter } from 'vs/workbench/parts/debug/ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeElement } from 'vs/base/browser/ui/tree/tree'; +import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { onUnexpectedError } from 'vs/base/common/errors'; From 42d0a188fce105d30c7a4388c6415c2e3183f375 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Jan 2019 18:15:53 +0100 Subject: [PATCH 0113/1066] extension tests: print errors to console --- src/vs/workbench/api/node/extHostExtensionService.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 2cf4f9efeed..e574496358e 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -572,6 +572,14 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { } private _handleExtensionTests(): Promise { + return this._doHandleExtensionTests().then(undefined, error => { + console.error(error); // ensure any error message makes it onto the console + + return Promise.reject(error); + }); + } + + private _doHandleExtensionTests(): Promise { if (!this._initData.environment.extensionTestsPath || !this._initData.environment.extensionDevelopmentLocationURI) { return Promise.resolve(undefined); } From 45b836d0b92263730ee6bddcd4978025b37a3d52 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 9 Jan 2019 11:40:15 -0800 Subject: [PATCH 0114/1066] #66141 - focus search after clearing query --- .../parts/preferences/electron-browser/settingsEditor2.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 033e107e95d..5fd660b0669 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -470,6 +470,7 @@ export class SettingsEditor2 extends BaseEditor { this._register(DOM.addDisposableListener(clearSearch, DOM.EventType.CLICK, (e: MouseEvent) => { DOM.EventHelper.stop(e, false); this.clearSearchResults(); + this.focusSearch(); })); DOM.append(this.noResultsMessage, clearSearchContainer); From 299997228dd3f0f4f652101f4418f001aa82a2f3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 11:50:43 -0800 Subject: [PATCH 0115/1066] Don't include object key types in completeFunctionCalls Fixes #66297 --- .../src/test/functionCallSnippet.test.ts | 8 ++++++++ .../src/utils/snippetForFunctionCall.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts index 1fc1fa67980..653890616e9 100644 --- a/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts +++ b/extensions/typescript-language-features/src/test/functionCallSnippet.test.ts @@ -117,4 +117,12 @@ suite('typescript function call snippets', () => { '\\$abc()$0'); }); + test('Should not include object key signature in completion, #66297', async () => { + assert.strictEqual( + snippetForFunctionCall( + { label: 'foobar', }, + [{ "text": "function", "kind": "keyword" }, { "text": " ", "kind": "space" }, { "text": "foobar", "kind": "functionName" }, { "text": "(", "kind": "punctuation" }, { "text": "param", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "{", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": " ", "kind": "space" }, { "text": "[", "kind": "punctuation" }, { "text": "key", "kind": "parameterName" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": "]", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "string", "kind": "keyword" }, { "text": ";", "kind": "punctuation" }, { "text": "\n", "kind": "lineBreak" }, { "text": "}", "kind": "punctuation" }, { "text": ")", "kind": "punctuation" }, { "text": ":", "kind": "punctuation" }, { "text": " ", "kind": "space" }, { "text": "void", "kind": "keyword" }] + ).snippet.value, + 'foobar(${1:param})$0'); + }); }); diff --git a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts index 7eab1e21ba9..fbf2868d101 100644 --- a/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts +++ b/extensions/typescript-language-features/src/utils/snippetForFunctionCall.ts @@ -68,7 +68,7 @@ function getParameterListParts( break; case PConst.DisplayPartKind.parameterName: - if (parenCount === 1 && isInMethod) { + if (parenCount === 1 && braceCount === 0 && isInMethod) { // Only take top level paren names const next = displayParts[i + 1]; // Skip optional parameters From 70696746b0aa3cfc1c635734009e308eb6136b63 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 9 Jan 2019 12:43:51 -0800 Subject: [PATCH 0116/1066] Address feedback --- .../server/src/cssServerMain.ts | 30 +++++-------------- .../server/src/{ => utils}/languageFacts.ts | 4 ++- 2 files changed, 11 insertions(+), 23 deletions(-) rename extensions/css-language-features/server/src/{ => utils}/languageFacts.ts (85%) diff --git a/extensions/css-language-features/server/src/cssServerMain.ts b/extensions/css-language-features/server/src/cssServerMain.ts index e376eaec816..0108e1df628 100644 --- a/extensions/css-language-features/server/src/cssServerMain.ts +++ b/extensions/css-language-features/server/src/cssServerMain.ts @@ -10,12 +10,12 @@ import URI from 'vscode-uri'; import * as fs from 'fs'; import { TextDocument, CompletionList } from 'vscode-languageserver-types'; -import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet } from 'vscode-css-languageservice'; +import { getCSSLanguageService, getSCSSLanguageService, getLESSLanguageService, LanguageSettings, LanguageService, Stylesheet, CSSData } 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 './languageFacts'; +import { parseCSSData } from './utils/languageFacts'; export interface Settings { css: LanguageSettings; @@ -67,26 +67,12 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { const dataPaths: string[] = params.initializationOptions.dataPaths; - let customData = { - customProperties: [], - customAtDirectives: [], - customPseudoElements: [], - customPseudoClasses: [] - }; + const customDataCollections: CSSData[] = []; dataPaths.forEach(p => { if (fs.existsSync(p)) { - const { - properties, - atDirectives, - pseudoClasses, - pseudoElements - } = parseCSSData(fs.readFileSync(p, 'utf-8')); - - customData.customProperties = customData.customProperties.concat(properties); - customData.customAtDirectives = customData.customAtDirectives.concat(atDirectives); - customData.customPseudoClasses = customData.customPseudoClasses.concat(pseudoClasses); - customData.customPseudoElements = customData.customPseudoElements.concat(pseudoElements); + const data = parseCSSData(fs.readFileSync(p, 'utf-8')); + customDataCollections.push(data); } else { return; } @@ -107,9 +93,9 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { scopedSettingsSupport = !!getClientCapability('workspace.configuration', false); foldingRangeLimit = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); - languageServices.css = getCSSLanguageService(customData); - languageServices.scss = getSCSSLanguageService(customData); - languageServices.less = getLESSLanguageService(customData); + languageServices.css = getCSSLanguageService({ customDataCollections }); + languageServices.scss = getSCSSLanguageService({ customDataCollections }); + languageServices.less = getLESSLanguageService({ customDataCollections }); 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/languageFacts.ts b/extensions/css-language-features/server/src/utils/languageFacts.ts similarity index 85% rename from extensions/css-language-features/server/src/languageFacts.ts rename to extensions/css-language-features/server/src/utils/languageFacts.ts index 3e2061101e3..c6d9b2ee641 100644 --- a/extensions/css-language-features/server/src/languageFacts.ts +++ b/extensions/css-language-features/server/src/utils/languageFacts.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export function parseCSSData(source: string) { +import { CSSData } from 'vscode-css-languageservice'; + +export function parseCSSData(source: string): CSSData { let rawData: any; try { From 7e86998ef91e348bc51065e7e527b006d5eeb3ff Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 12:58:20 -0800 Subject: [PATCH 0117/1066] Fixes #66247 Allow markdown comments to match blocks that contain extra docs on first line --- .../syntaxes/MarkdownDocumentationInjection.tmLanguage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-basics/syntaxes/MarkdownDocumentationInjection.tmLanguage.json b/extensions/typescript-basics/syntaxes/MarkdownDocumentationInjection.tmLanguage.json index d7711577168..4794b38e2e1 100644 --- a/extensions/typescript-basics/syntaxes/MarkdownDocumentationInjection.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/MarkdownDocumentationInjection.tmLanguage.json @@ -7,7 +7,7 @@ ], "repository": { "markdown-comment": { - "begin": "(?<=/\\*\\*)\\s*$", + "begin": "(?<=/\\*\\*)([^*]|\\*(?!/))*$", "while": "(^|\\G)\\s*\\*(?!/)(?=([^*]|[*](?!/))*$)", "patterns": [ { From 5b658e7d602106a93cb5c21cdc6f315637ab432d Mon Sep 17 00:00:00 2001 From: Reece Dunham Date: Wed, 9 Jan 2019 18:07:26 -0500 Subject: [PATCH 0118/1066] Update contributing guidelines; some issues may be OS specific (#66289) --- CONTRIBUTING.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7775753e8bc..34703fae665 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,6 @@ If you find your issue already exists, make relevant comments and add your [reac * 👍 - upvote * 👎 - downvote - If you cannot find an existing issue that describes your bug or feature, create a new issue using the guidelines below. ### Writing Good Bug Reports and Feature Requests @@ -52,7 +51,9 @@ The built-in tool for reporting an issue, which you can access by using `Report Please include the following with each issue: -* Version of VS Code +* Version of VS Code + +* Your operating system * List of extensions that you have installed From 9efac59949e9cff8140560555e0a552e56fe1e7f Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 9 Jan 2019 15:55:55 -0800 Subject: [PATCH 0119/1066] Improve custom data reading --- .../client/src/cssMain.ts | 17 +------- .../client/src/customData.ts | 39 +++++++++++++++++++ extensions/css-language-features/package.json | 4 +- 3 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 extensions/css-language-features/client/src/customData.ts diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index fbbec3cee86..7520e61b209 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -11,6 +11,7 @@ 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 { getCustomDataPathsInAllWorkspaces } from './customData'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { @@ -30,21 +31,7 @@ export function activate(context: ExtensionContext) { let documentSelector = ['css', 'scss', 'less']; - let dataPaths: string[] = workspace.getConfiguration('css').get('experimental.customData', []); - if (dataPaths && dataPaths.length > 0) { - if (!workspace.workspaceFolders) { - dataPaths = []; - } else { - try { - const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath; - dataPaths = dataPaths.map(d => { - return path.resolve(workspaceRoot, d); - }); - } catch (err) { - dataPaths = []; - } - } - } + let dataPaths = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); // Options to control the language client let clientOptions: LanguageClientOptions = { diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts new file mode 100644 index 00000000000..25cc95ccc92 --- /dev/null +++ b/extensions/css-language-features/client/src/customData.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { workspace, WorkspaceFolder } from 'vscode'; + +interface ExperimentalConfig { + experimental?: { + customData?: string[] + }; +} + +export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined): string[] { + const dataPaths: string[] = []; + + if (!workspaceFolders) { + return dataPaths; + } + + workspaceFolders.forEach(wf => { + const allCssConfig = workspace.getConfiguration(undefined, wf.uri); + const wfCSSConfig = allCssConfig.inspect('css'); + if ( + wfCSSConfig && + wfCSSConfig.workspaceFolderValue && + wfCSSConfig.workspaceFolderValue.experimental && + wfCSSConfig.workspaceFolderValue.experimental.customData + ) { + + wfCSSConfig.workspaceFolderValue.experimental.customData.forEach(p => [ + dataPaths.push(path.resolve(wf.uri.fsPath, p)) + ]); + } + }); + + return dataPaths; +} diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 43c2fcb26f7..abb927c319c 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -34,7 +34,9 @@ "properties": { "css.experimental.customData": { "type": "array", - "description": "A list of JSON file paths that define custom CSS data that loads custom properties, at directives, pseudo classes / elements." + "description": "A list of JSON file paths that define custom CSS data that loads custom properties, at directives, pseudo classes / elements.", + "default": [], + "scope": "resource" }, "css.validate": { "type": "boolean", From 177b8051501f1e7ace91dd16060c1691678b45ec Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 9 Jan 2019 16:16:04 -0800 Subject: [PATCH 0120/1066] Handle workspace folder reading --- .../client/src/customData.ts | 56 +++++++++++++++++++ .../client/src/htmlMain.ts | 19 +------ .../html-language-features/package.json | 4 +- 3 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 extensions/html-language-features/client/src/customData.ts diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts new file mode 100644 index 00000000000..5e7c1c99c16 --- /dev/null +++ b/extensions/html-language-features/client/src/customData.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { workspace, WorkspaceFolder } from 'vscode'; + +interface ExperimentalConfig { + experimental?: { + custom?: { + tags?: string[]; + attributes?: string[]; + } + }; +} + +export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined) { + const tagPaths: string[] = []; + const attributePaths: string[] = []; + + if (!workspaceFolders) { + return { + tagPaths, + attributePaths + }; + } + + workspaceFolders.forEach(wf => { + const allHtmlConfig = workspace.getConfiguration(undefined, wf.uri); + const wfHtmlConfig = allHtmlConfig.inspect('html'); + + if ( + wfHtmlConfig && + wfHtmlConfig.workspaceFolderValue && + wfHtmlConfig.workspaceFolderValue.experimental && + wfHtmlConfig.workspaceFolderValue.experimental.custom + ) { + if (wfHtmlConfig.workspaceFolderValue.experimental.custom.tags) { + wfHtmlConfig.workspaceFolderValue.experimental.custom.tags.forEach(t => { + tagPaths.push(path.resolve(wf.uri.fsPath, t)); + }); + } + if (wfHtmlConfig.workspaceFolderValue.experimental.custom.attributes) { + wfHtmlConfig.workspaceFolderValue.experimental.custom.attributes.forEach(a => { + attributePaths.push(path.resolve(wf.uri.fsPath, a)); + }); + } + } + }); + + return { + tagPaths, + attributePaths + }; +} diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 0b84091bed7..0b4a33b776b 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -13,6 +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'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -49,23 +50,7 @@ export function activate(context: ExtensionContext) { let documentSelector = ['html', 'handlebars', 'razor']; let embeddedLanguages = { css: true, javascript: true }; - let tagPaths: string[] = workspace.getConfiguration('html').get('experimental.custom.tags', []); - let attributePaths: string[] = workspace.getConfiguration('html').get('experimental.custom.attributes', []); - - if (tagPaths && tagPaths.length > 0) { - if (!workspace.workspaceFolders) { - tagPaths = []; - } else { - try { - const workspaceRoot = workspace.workspaceFolders[0].uri.fsPath; - tagPaths = tagPaths.map(d => { - return path.resolve(workspaceRoot, d); - }); - } catch (err) { - tagPaths = []; - } - } - } + let { tagPaths, attributePaths } = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); // Options to control the language client let clientOptions: LanguageClientOptions = { diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 78236179394..c456a240ff0 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -33,11 +33,11 @@ "properties": { "html.experimental.custom.tags": { "type": "array", - "description": "A list of JSON file paths that define custom tags." + "description": "A list of JSON file paths that define custom tags. Only workspace folder setting will be read." }, "html.experimental.custom.attributes": { "type": "array", - "description": "A list of JSON file paths that define custom attributes." + "description": "A list of JSON file paths that define custom attributes. Only workspace folder setting will be read." }, "html.format.enable": { "type": "boolean", From 3580109d0531d3cc0810909eaa1bf1760109403a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 13:31:12 -0800 Subject: [PATCH 0121/1066] Pick up new TS version --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index 9d944cd3ec7..c162a8f58f5 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.3.0-dev.20190101" + "typescript": "3.3.0-dev.20190108" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 4e2a9131776..7ba5f1f77f8 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.3.0-dev.20190101: - version "3.3.0-dev.20190101" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.0-dev.20190101.tgz#2eecb7a9c6c63d447615b6e6b2d6e648cfdc4caa" - integrity sha512-pmpqjyS64DIfnCJ8XGFXlTC+g3hCud69EYEudjS/alwlVx2qedQedY36ASHPRopiZxJcwHCkM/CypDiQtfQZVw== +typescript@3.3.0-dev.20190108: + version "3.3.0-dev.20190108" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.0-dev.20190108.tgz#4ac2e3f7b619a961bfae0750412bf9e79fafe720" + integrity sha512-GNGczOoEyZtqbY72G6fNnyCsgSw/gSLyVxHfduDalKl0tOrIt+mEV5JyVQzEMsmcyO821/3QsCjulJHS5KFOWg== From 2c3351df995aa7e22117d2e651df7ab13441aa0f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 14:18:39 -0800 Subject: [PATCH 0122/1066] Use CharacterSet --- src/vs/editor/contrib/suggest/suggestController.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 7d20441b06a..bccb74f8083 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -31,12 +31,13 @@ import { WordContextKey } from 'vs/editor/contrib/suggest/wordContextKey'; import { Event } from 'vs/base/common/event'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IdleValue } from 'vs/base/common/async'; +import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; class AcceptOnCharacterOracle { private _disposables: IDisposable[] = []; - private _activeAcceptCharacters = new Set(); + private _activeAcceptCharacters = new CharacterSet(); private _activeItem: ISelectedSuggestion; constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) { @@ -47,7 +48,7 @@ class AcceptOnCharacterOracle { this._disposables.push(editor.onWillType(text => { if (this._activeItem) { - const ch = text[text.length - 1]; + const ch = text.charCodeAt(text.length - 1); if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) { accept(this._activeItem); } @@ -61,10 +62,10 @@ class AcceptOnCharacterOracle { return; } this._activeItem = selected; - this._activeAcceptCharacters.clear(); + this._activeAcceptCharacters = new CharacterSet(); for (const ch of selected.item.completion.commitCharacters) { if (ch.length > 0) { - this._activeAcceptCharacters.add(ch[0]); + this._activeAcceptCharacters.add(ch.charCodeAt(0)); } } } From b99471e29c10e0f990943abb6fe37733a658ef47 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 14:21:31 -0800 Subject: [PATCH 0123/1066] Use more explicit state for CharacterOracle --- .../contrib/suggest/suggestController.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index bccb74f8083..9e899b3aa91 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -37,8 +37,10 @@ class AcceptOnCharacterOracle { private _disposables: IDisposable[] = []; - private _activeAcceptCharacters = new CharacterSet(); - private _activeItem: ISelectedSuggestion; + private _active?: { + readonly acceptCharacters: CharacterSet; + readonly item: ISelectedSuggestion; + }; constructor(editor: ICodeEditor, widget: SuggestWidget, accept: (selected: ISelectedSuggestion) => any) { @@ -47,10 +49,10 @@ class AcceptOnCharacterOracle { this._disposables.push(widget.onDidHide(this.reset, this)); this._disposables.push(editor.onWillType(text => { - if (this._activeItem) { + if (this._active) { const ch = text.charCodeAt(text.length - 1); - if (this._activeAcceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) { - accept(this._activeItem); + if (this._active.acceptCharacters.has(ch) && editor.getConfiguration().contribInfo.acceptSuggestionOnCommitCharacter) { + accept(this._active.item); } } })); @@ -61,17 +63,18 @@ class AcceptOnCharacterOracle { this.reset(); return; } - this._activeItem = selected; - this._activeAcceptCharacters = new CharacterSet(); + + const acceptCharacters = new CharacterSet(); for (const ch of selected.item.completion.commitCharacters) { if (ch.length > 0) { - this._activeAcceptCharacters.add(ch.charCodeAt(0)); + acceptCharacters.add(ch.charCodeAt(0)); } } + this._active = { acceptCharacters, item: selected }; } reset(): void { - this._activeItem = undefined; + this._active = undefined; } dispose() { From f72fe8e45281bdfa4457dc66826508524066c41f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 15:10:55 -0800 Subject: [PATCH 0124/1066] Strict null auto add --- src/tsconfig.strictNullChecks.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index ebf46a30d21..d8e35e83f6b 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -243,13 +243,17 @@ "./vs/editor/contrib/rename/renameInputField.ts", "./vs/editor/contrib/snippet/snippetController2.ts", "./vs/editor/contrib/snippet/snippetParser.ts", + "./vs/editor/contrib/snippet/snippetSession.ts", "./vs/editor/contrib/snippet/snippetVariables.ts", + "./vs/editor/contrib/snippet/test/snippetController2.old.test.ts", + "./vs/editor/contrib/snippet/test/snippetController2.test.ts", "./vs/editor/contrib/snippet/test/snippetParser.test.ts", "./vs/editor/contrib/snippet/test/snippetVariables.test.ts", "./vs/editor/contrib/suggest/completionModel.ts", "./vs/editor/contrib/suggest/suggest.ts", "./vs/editor/contrib/suggest/suggestAlternatives.ts", "./vs/editor/contrib/suggest/suggestMemory.ts", + "./vs/editor/contrib/suggest/suggestModel.ts", "./vs/editor/contrib/suggest/suggestWidget.ts", "./vs/editor/contrib/suggest/test/completionModel.test.ts", "./vs/editor/contrib/suggest/test/suggest.test.ts", @@ -874,4 +878,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} +} \ No newline at end of file From 7a7ccce37a7c78251153a052a8f03995a2026d05 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 15:33:42 -0800 Subject: [PATCH 0125/1066] Working on strict null checking with `Map.get` may return undefined --- src/vs/base/browser/ui/list/listView.ts | 15 ++++++++++----- src/vs/base/browser/ui/list/rowCache.ts | 12 ++++++++++-- src/vs/base/browser/ui/menu/menu.ts | 6 +++--- src/vs/base/browser/ui/menu/menubar.ts | 4 ++-- src/vs/base/common/map.ts | 2 +- src/vs/base/node/stats.ts | 5 ++--- src/vs/base/parts/ipc/node/ipc.ts | 6 ++++++ .../contrib/smartSelect/bracketSelections.ts | 4 ++-- .../configuration/common/configurationModels.ts | 10 +++++----- .../instantiation/common/instantiation.ts | 2 +- .../workbench/common/editor/dataUriEditorInput.ts | 12 ++++++------ src/vs/workbench/parts/output/common/output.ts | 4 ++-- .../workbench/parts/search/common/searchModel.ts | 9 +++++---- .../configuration/common/configurationModels.ts | 6 +++++- .../node/extensionDescriptionRegistry.ts | 14 +++++--------- .../textMate/electron-browser/TMSyntax.ts | 8 ++++++-- 16 files changed, 71 insertions(+), 48 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index aa98aae01d0..8a9bf0026c1 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -468,7 +468,9 @@ export class ListView implements ISpliceable, IDisposable { this.updateItemInDOM(item, index); const renderer = this.renderers.get(item.templateId); - renderer.renderElement(item.element, index, item.row.templateData); + if (renderer) { + renderer.renderElement(item.element, index, item.row.templateData); + } const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); @@ -500,8 +502,7 @@ export class ListView implements ISpliceable, IDisposable { item.dragStartDisposable.dispose(); const renderer = this.renderers.get(item.templateId); - - if (renderer.disposeElement) { + if (renderer && renderer.disposeElement) { renderer.disposeElement(item.element, index, item.row!.templateData); } @@ -917,7 +918,9 @@ export class ListView implements ISpliceable, IDisposable { row.domNode!.style.height = ''; this.rowsContainer.appendChild(row.domNode!); - renderer.renderElement(item.element, index, row.templateData); + if (renderer) { + renderer.renderElement(item.element, index, row.templateData); + } item.size = row.domNode!.offsetHeight; item.renderWidth = this.renderWidth; this.rowsContainer.removeChild(row.domNode!); @@ -953,7 +956,9 @@ export class ListView implements ISpliceable, IDisposable { for (const item of this.items) { if (item.row) { const renderer = this.renderers.get(item.row.templateId); - renderer.disposeTemplate(item.row.templateData); + if (renderer) { + renderer.disposeTemplate(item.row.templateData); + } } } diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index b7007c2fc0f..dd54c73a0a7 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -38,7 +38,7 @@ export class RowCache implements IDisposable { if (!result) { const domNode = $('.monaco-list-row'); - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); const templateData = renderer.renderTemplate(domNode); result = { domNode, templateId, templateData }; } @@ -86,7 +86,7 @@ export class RowCache implements IDisposable { this.cache.forEach((cachedRows, templateId) => { for (const cachedRow of cachedRows) { - const renderer = this.renderers.get(templateId); + const renderer = this.getRenderer(templateId); renderer.disposeTemplate(cachedRow.templateData); cachedRow.domNode = null; cachedRow.templateData = null; @@ -101,4 +101,12 @@ export class RowCache implements IDisposable { this.cache.clear(); this.renderers = null!; // StrictNullOverride: nulling out ok in dispose } + + private getRenderer(templateId: string): IListRenderer { + const renderer = this.renderers.get(templateId); + if (!renderer) { + throw new Error(`No renderer found for ${templateId}`); + } + return renderer; + } } \ No newline at end of file diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index b3f58c7084b..de0c89c6b77 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -96,7 +96,7 @@ export class Menu extends ActionBar { const key = KeyCodeUtils.fromString(e.key); if (this.mnemonics.has(key)) { EventHelper.stop(e, true); - const actions = this.mnemonics.get(key); + const actions = this.mnemonics.get(key)!; if (actions.length === 1) { if (actions[0] instanceof SubmenuActionItem) { @@ -292,7 +292,7 @@ export class Menu extends ActionBar { if (mnemonic && menuActionItem.isEnabled()) { let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionItems = this.mnemonics.get(mnemonic)!; } actionItems.push(menuActionItem); @@ -322,7 +322,7 @@ export class Menu extends ActionBar { if (mnemonic && menuActionItem.isEnabled()) { let actionItems: MenuActionItem[] = []; if (this.mnemonics.has(mnemonic)) { - actionItems = this.mnemonics.get(mnemonic); + actionItems = this.mnemonics.get(mnemonic)!; } actionItems.push(menuActionItem); diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index b5450749148..0378c58d62f 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -118,7 +118,7 @@ export class MenuBar extends Disposable { } else if (event.equals(KeyCode.Escape) && this.isFocused && !this.isOpen) { this.setUnfocusedState(); } else if (!this.isOpen && !event.ctrlKey && this.options.enableMnemonics && this.mnemonicsInUse && this.mnemonics.has(key)) { - const menuIndex = this.mnemonics.get(key); + const menuIndex = this.mnemonics.get(key)!; this.onMenuTriggered(menuIndex, false); } else { eventHandled = false; @@ -171,7 +171,7 @@ export class MenuBar extends Disposable { this.mnemonicsInUse = true; this.updateMnemonicVisibility(true); - const menuIndex = this.mnemonics.get(key); + const menuIndex = this.mnemonics.get(key)!; this.onMenuTriggered(menuIndex, false); })); diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 05aed148d23..00b15a28b26 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -431,7 +431,7 @@ export class ResourceMap { this.map.set(this.toKey(resource), value); } - get(resource: URI): T { + get(resource: URI): T | undefined { return this.map.get(this.toKey(resource)); } diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index fbf895ea0a6..8018b64788f 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -49,9 +49,8 @@ export function collectLaunchConfigs(folder: string): Promise implements IChannelServer; @@ -325,6 +328,9 @@ export class ChannelServer implements IChannelServer { - const { contents, overrides, keys } = this._folderConfigurations.get(folder); + const { contents, overrides, keys } = this._folderConfigurations.get(folder)!; result[folder.toString()] = { contents, overrides, keys }; return result; }, Object.create({})), @@ -498,7 +498,7 @@ export class Configuration { addKeys(keys.user); addKeys(keys.workspace); for (const resource of this.folders.keys()) { - addKeys(this.folders.get(resource).keys); + addKeys(this.folders.get(resource)!.keys); } return all; } @@ -554,7 +554,7 @@ export class ConfigurationChangeEvent extends AbstractConfigurationChangeEvent i this._changedConfiguration = this._changedConfiguration.merge(arg1._changedConfiguration); for (const resource of arg1._changedConfigurationByResource.keys()) { let changedConfigurationByResource = this.getOrSetChangedConfigurationForResource(resource); - changedConfigurationByResource = changedConfigurationByResource.merge(arg1._changedConfigurationByResource.get(resource)); + changedConfigurationByResource = changedConfigurationByResource.merge(arg1._changedConfigurationByResource.get(resource)!); this._changedConfigurationByResource.set(resource, changedConfigurationByResource); } } else { diff --git a/src/vs/platform/instantiation/common/instantiation.ts b/src/vs/platform/instantiation/common/instantiation.ts index 3f7cd9f2d11..3fa2be6556a 100644 --- a/src/vs/platform/instantiation/common/instantiation.ts +++ b/src/vs/platform/instantiation/common/instantiation.ts @@ -128,7 +128,7 @@ function storeServiceDependency(id: Function, target: Function, index: number, o export function createDecorator(serviceId: string): { (...args: any[]): void; type: T; } { if (_util.serviceIds.has(serviceId)) { - return _util.serviceIds.get(serviceId); + return _util.serviceIds.get(serviceId)!; } const id = function (target: Function, key: string, index: number): any { diff --git a/src/vs/workbench/common/editor/dataUriEditorInput.ts b/src/vs/workbench/common/editor/dataUriEditorInput.ts index b807cbcdb1c..d41c7e63683 100644 --- a/src/vs/workbench/common/editor/dataUriEditorInput.ts +++ b/src/vs/workbench/common/editor/dataUriEditorInput.ts @@ -18,8 +18,8 @@ export class DataUriEditorInput extends EditorInput { static readonly ID: string = 'workbench.editors.dataUriEditorInput'; private resource: URI; - private name: string; - private description: string; + private readonly name: string | undefined; + private readonly description: string | undefined; constructor( name: string, @@ -54,12 +54,12 @@ export class DataUriEditorInput extends EditorInput { return DataUriEditorInput.ID; } - getName(): string { - return this.name; + getName(): string | null { + return this.name || null; } - getDescription(): string { - return this.description; + getDescription(): string | null { + return this.description || null; } resolve(): Promise { diff --git a/src/vs/workbench/parts/output/common/output.ts b/src/vs/workbench/parts/output/common/output.ts index 4a96b390a3a..6fee8571bbe 100644 --- a/src/vs/workbench/parts/output/common/output.ts +++ b/src/vs/workbench/parts/output/common/output.ts @@ -155,7 +155,7 @@ export interface IOutputChannelRegistry { /** * Returns the channel with the passed id. */ - getChannel(id: string): IOutputChannelDescriptor; + getChannel(id: string): IOutputChannelDescriptor | undefined; /** * Remove the output channel with the passed id. @@ -185,7 +185,7 @@ class OutputChannelRegistry implements IOutputChannelRegistry { return result; } - public getChannel(id: string): IOutputChannelDescriptor { + public getChannel(id: string): IOutputChannelDescriptor | undefined { return this.channels.get(id); } diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index 6a7c5c20355..c6582f58457 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -457,8 +457,8 @@ export class BaseFolderMatch extends Disposable { const added: FileMatch[] = []; const updated: FileMatch[] = []; raw.forEach(rawFileMatch => { - if (this._fileMatches.has(rawFileMatch.resource)) { - const existingFileMatch = this._fileMatches.get(rawFileMatch.resource); + const existingFileMatch = this._fileMatches.get(rawFileMatch.resource); + if (existingFileMatch) { rawFileMatch .results! .filter(resultIsMatch) @@ -691,8 +691,9 @@ export class SearchResult extends Disposable { return; } - if (folderMatch.resource()) { - rawPerFolder.get(folderMatch.resource()!).push(rawFileMatch); + const resource = folderMatch.resource(); + if (resource) { + rawPerFolder.get(resource)!.push(rawFileMatch); } else { otherFileMatches.push(rawFileMatch); } diff --git a/src/vs/workbench/services/configuration/common/configurationModels.ts b/src/vs/workbench/services/configuration/common/configurationModels.ts index 98e9f715d79..a943fab87fe 100644 --- a/src/vs/workbench/services/configuration/common/configurationModels.ts +++ b/src/vs/workbench/services/configuration/common/configurationModels.ts @@ -202,7 +202,11 @@ export class Configuration extends BaseConfiguration { // Do not remove workspace configuration return new ConfigurationChangeEvent(); } - const keys = this.folders.get(folder).keys; + const folderConfig = this.folders.get(folder); + if (!folderConfig) { + throw new Error('Unknown folder'); + } + const keys = folderConfig.keys; super.deleteFolderConfiguration(folder); return new ConfigurationChangeEvent().change(keys, folder); } diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 2f350e558bb..3f5995e982e 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -46,7 +46,7 @@ export class ExtensionDescriptionRegistry { if (!this._activationMap.has(activationEvent)) { this._activationMap.set(activationEvent, []); } - this._activationMap.get(activationEvent).push(extensionDescription); + this._activationMap.get(activationEvent)!.push(extensionDescription); } } } @@ -69,10 +69,8 @@ export class ExtensionDescriptionRegistry { } public getExtensionDescriptionsForActivationEvent(activationEvent: string): IExtensionDescription[] { - if (!this._activationMap.has(activationEvent)) { - return []; - } - return this._activationMap.get(activationEvent).slice(0); + const extensions = this._activationMap.get(activationEvent); + return extensions ? extensions.slice(0) : []; } public getAllExtensionDescriptions(): IExtensionDescription[] { @@ -80,9 +78,7 @@ export class ExtensionDescriptionRegistry { } public getExtensionDescription(extensionId: ExtensionIdentifier | string): IExtensionDescription | null { - if (!this._extensionsMap.has(ExtensionIdentifier.toKey(extensionId))) { - return null; - } - return this._extensionsMap.get(ExtensionIdentifier.toKey(extensionId)); + const extension = this._extensionsMap.get(ExtensionIdentifier.toKey(extensionId)); + return extension ? extension : null; } } diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 594d0b56c9e..520ba3d13a4 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -390,8 +390,12 @@ export class TextMateService extends Disposable implements ITextMateService { } private _createGrammar(modeId: string): Promise { - let scopeName = this._languageToScope.get(modeId); - let languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); + const scopeName = this._languageToScope.get(modeId); + if (typeof scopeName !== 'string') { + // No TM grammar defined + return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); + } + const languageRegistration = this._scopeRegistry.getLanguageRegistration(scopeName); if (!languageRegistration) { // No TM grammar defined return Promise.reject(new Error(nls.localize('no-tm-grammar', "No TM Grammar registered for this language."))); From c5d160afae9dbc5e09c13ac562180db8dfcf7dbc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 15:35:25 -0800 Subject: [PATCH 0126/1066] Use for-of loop --- .../extensions/node/extensionDescriptionRegistry.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 3f5995e982e..506e26738de 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -22,9 +22,7 @@ export class ExtensionDescriptionRegistry { this._extensionsArr = []; this._activationMap = new Map(); - for (let i = 0, len = this._extensionDescriptions.length; i < len; i++) { - let extensionDescription = this._extensionDescriptions[i]; - + for (const extensionDescription of this._extensionDescriptions) { if (this._extensionsMap.has(ExtensionIdentifier.toKey(extensionDescription.identifier))) { // No overwriting allowed! console.error('Extension `' + extensionDescription.identifier.value + '` is already registered'); @@ -35,9 +33,7 @@ export class ExtensionDescriptionRegistry { this._extensionsArr.push(extensionDescription); if (Array.isArray(extensionDescription.activationEvents)) { - for (let j = 0, lenJ = extensionDescription.activationEvents.length; j < lenJ; j++) { - let activationEvent = extensionDescription.activationEvents[j]; - + for (let activationEvent of extensionDescription.activationEvents) { // TODO@joao: there's no easy way to contribute this if (activationEvent === 'onUri') { activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; @@ -53,7 +49,7 @@ export class ExtensionDescriptionRegistry { } public keepOnly(extensionIds: ExtensionIdentifier[]): void { - let toKeep = new Set(); + const toKeep = new Set(); extensionIds.forEach(extensionId => toKeep.add(ExtensionIdentifier.toKey(extensionId))); this._extensionDescriptions = this._extensionDescriptions.filter(extension => toKeep.has(ExtensionIdentifier.toKey(extension.identifier))); this._initialize(); From f89f4902cad5133470cf0115a04b615de619cebd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 15:57:20 -0800 Subject: [PATCH 0127/1066] Continue with strict null fixes for Map.get possibly returning undefined --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ++-- src/vs/base/browser/ui/tree/tree.ts | 5 +++-- src/vs/base/node/ps.ts | 2 +- src/vs/base/node/stats.ts | 4 ++-- src/vs/base/test/browser/ui/grid/grid.test.ts | 6 +++++- .../editor/common/services/getIconClasses.ts | 6 +++--- .../services/markerDecorationsServiceImpl.ts | 4 ++-- .../contrib/smartSelect/bracketSelections.ts | 4 ++-- .../api/node/extHostExtensionActivator.ts | 21 +++++++++++-------- src/vs/workbench/browser/parts/views/views.ts | 3 +++ .../common/editor/binaryEditorModel.ts | 2 +- .../parts/comments/common/commentModel.ts | 6 +++--- .../parts/markers/electron-browser/markers.ts | 6 +++--- .../electron-browser/snippetsService.ts | 4 ++-- 14 files changed, 44 insertions(+), 33 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index fded659d85d..91ea190331c 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -450,9 +450,9 @@ export class AsyncDataTree implements IDisposable // Implementation private getDataNode(element: TInput | T): IAsyncDataTreeNode { - const node: IAsyncDataTreeNode = this.nodes.get((element === this.root.element ? null : element) as T); + const node: IAsyncDataTreeNode | undefined = this.nodes.get((element === this.root.element ? null : element) as T); - if (typeof node === 'undefined') { + if (!node) { throw new Error(`Data tree node not found: ${element}`); } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 10078590931..dea1716ebb0 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -7,6 +7,7 @@ import { Event } from 'vs/base/common/event'; import { Iterator } from 'vs/base/common/iterator'; import { IListRenderer, AbstractListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { coalesce } from 'vs/base/common/arrays'; export const enum TreeVisibility { @@ -192,9 +193,9 @@ export abstract class AbstractTreeRenderer this.elementsToNodes.get(e) || null).filter(e => e !== null); + return coalesce(e.map(e => this.elementsToNodes.get(e))); } else { - return this.elementsToNodes.get(e) || null; + return this.elementsToNodes.get(e); } })); } diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 7d7d0432428..fd39b2ce4a2 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -22,7 +22,7 @@ export function listProcesses(rootPid: number): Promise { return new Promise((resolve, reject) => { - let rootItem: ProcessItem; + let rootItem: ProcessItem | undefined; const map = new Map(); function addToTree(pid: number, ppid: number, cmd: string, load: number, mem: number) { diff --git a/src/vs/base/node/stats.ts b/src/vs/base/node/stats.ts index 8018b64788f..ae76a9de218 100644 --- a/src/vs/base/node/stats.ts +++ b/src/vs/base/node/stats.ts @@ -150,7 +150,7 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise let addFileType = (fileType: string) => { if (fileTypes.has(fileType)) { - fileTypes.set(fileType, fileTypes.get(fileType) + 1); + fileTypes.set(fileType, fileTypes.get(fileType)! + 1); } else { fileTypes.set(fileType, 1); @@ -161,7 +161,7 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise for (const each of configFilePatterns) { if (each.pattern.test(fileName)) { if (configFiles.has(each.tag)) { - configFiles.set(each.tag, configFiles.get(each.tag) + 1); + configFiles.set(each.tag, configFiles.get(each.tag)! + 1); } else { configFiles.set(each.tag, 1); } diff --git a/src/vs/base/test/browser/ui/grid/grid.test.ts b/src/vs/base/test/browser/ui/grid/grid.test.ts index 58729c4fe51..7385b3ca4d6 100644 --- a/src/vs/base/test/browser/ui/grid/grid.test.ts +++ b/src/vs/base/test/browser/ui/grid/grid.test.ts @@ -469,7 +469,11 @@ class TestViewDeserializer implements IViewDeserializer { } getView(id: string): TestSerializableView { - return this.views.get(id); + const view = this.views.get(id); + if (!view) { + throw new Error('Unknown view'); + } + return view; } } diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/services/getIconClasses.ts index 09da432e4f2..d8f7d5bf122 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/services/getIconClasses.ts @@ -16,8 +16,8 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon']; if (resource) { // Get the path and name of the resource. For data-URIs, we need to parse specially - let name: string; - let path: string; + let name: string | undefined; + let path: string | undefined; if (resource.scheme === Schemas.data) { const metadata = DataUri.parseMetaData(resource); name = metadata.get(DataUri.META_DATA_LABEL); @@ -44,7 +44,7 @@ export function getIconClasses(modelService: IModelService, modeService: IModeSe } // Configured Language let configuredLangId: string | null = getConfiguredLangId(modelService, resource); - configuredLangId = configuredLangId || modeService.getModeIdByFilepathOrFirstLine(path); + configuredLangId = configuredLangId || (path ? modeService.getModeIdByFilepathOrFirstLine(path) : null); if (configuredLangId) { classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`); } diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index d5f9583c7e3..ecf4af882d6 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -41,7 +41,7 @@ class MarkerDecorations extends Disposable { } } - getMarker(decoration: IModelDecoration): IMarker | null { + getMarker(decoration: IModelDecoration): IMarker | undefined { return this._markersData.get(decoration.id); } } @@ -65,7 +65,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null { const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri)); - return markerDecorations ? markerDecorations.getMarker(decoration) : null; + return markerDecorations ? markerDecorations.getMarker(decoration) || null : null; } private _handleMarkerChange(changedResources: URI[]): void { diff --git a/src/vs/editor/contrib/smartSelect/bracketSelections.ts b/src/vs/editor/contrib/smartSelect/bracketSelections.ts index fc2ea440e42..c69442aaf73 100644 --- a/src/vs/editor/contrib/smartSelect/bracketSelections.ts +++ b/src/vs/editor/contrib/smartSelect/bracketSelections.ts @@ -92,11 +92,11 @@ export class BracketSelectionRangeProvider implements SelectionRangeProvider { const key = bracket.close; if (!bracket.isOpen) { // wait for opening - let val = counts.has(key) ? counts.get(key) : 0; + let val = counts.has(key) ? counts.get(key)! : 0; counts.set(key, val + 1); } else { // opening - let val = counts.has(key) ? counts.get(key) : 0; + let val = counts.has(key) ? counts.get(key)! : 0; val -= 1; counts.set(key, Math.max(0, val)); if (val < 0) { diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index 6b776d231f5..dba9d4f2821 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -216,10 +216,11 @@ export class ExtensionsActivator { public getActivatedExtension(extensionId: ExtensionIdentifier): ActivatedExtension { const extensionKey = ExtensionIdentifier.toKey(extensionId); - if (!this._activatedExtensions.has(extensionKey)) { + const activatedExtension = this._activatedExtensions.get(extensionKey); + if (!activatedExtension) { throw new Error('Extension `' + extensionId.value + '` is not known or not activated'); } - return this._activatedExtensions.get(extensionKey); + return activatedExtension; } public activateByEvent(activationEvent: string, reason: ExtensionActivationReason): Promise { @@ -261,8 +262,8 @@ export class ExtensionsActivator { return; } - if (this._activatedExtensions.has(ExtensionIdentifier.toKey(depId))) { - let dep = this._activatedExtensions.get(ExtensionIdentifier.toKey(depId)); + const dep = this._activatedExtensions.get(ExtensionIdentifier.toKey(depId)); + if (dep) { if (dep.activationFailed) { // Error condition 2: a dependency has already failed activation this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); @@ -344,11 +345,12 @@ export class ExtensionsActivator { return Promise.resolve(undefined); } - if (this._activatingExtensions.has(extensionKey)) { - return this._activatingExtensions.get(extensionKey); + const currentlyActivatingExtension = this._activatingExtensions.get(extensionKey); + if (currentlyActivatingExtension) { + return currentlyActivatingExtension; } - this._activatingExtensions.set(extensionKey, this._host.actualActivateExtension(extensionDescription, reason).then(undefined, (err) => { + const newlyActivatingExtension = this._host.actualActivateExtension(extensionDescription, reason).then(undefined, (err) => { this._host.showMessage(Severity.Error, nls.localize('activationError', "Activating extension '{0}' failed: {1}.", extensionDescription.identifier.value, err.message)); console.error('Activating extension `' + extensionDescription.identifier.value + '` failed: ', err.message); console.log('Here is the error stack: ', err.stack); @@ -357,8 +359,9 @@ export class ExtensionsActivator { }).then((x: ActivatedExtension) => { this._activatedExtensions.set(extensionKey, x); this._activatingExtensions.delete(extensionKey); - })); + }); - return this._activatingExtensions.get(extensionKey); + this._activatingExtensions.set(extensionKey, newlyActivatingExtension); + return newlyActivatingExtension; } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 131f1d709c3..8182e15a11b 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -312,6 +312,9 @@ export class ContributableViewsModel extends Disposable { for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) { const viewDescriptor = this.viewDescriptors[i]; const state = this.viewStates.get(viewDescriptor.id); + if (!state) { + throw new Error(`View state for ${id} not found`); + } if (viewDescriptor.id === id) { return { index: i, visibleIndex, viewDescriptor, state }; diff --git a/src/vs/workbench/common/editor/binaryEditorModel.ts b/src/vs/workbench/common/editor/binaryEditorModel.ts index 9087c09cfcc..57f46840097 100644 --- a/src/vs/workbench/common/editor/binaryEditorModel.ts +++ b/src/vs/workbench/common/editor/binaryEditorModel.ts @@ -35,7 +35,7 @@ export class BinaryEditorModel extends EditorModel { this.size = Number(metadata.get(DataUri.META_DATA_SIZE)); } - this.mime = metadata.get(DataUri.META_DATA_MIME); + this.mime = metadata.get(DataUri.META_DATA_MIME)!; } } diff --git a/src/vs/workbench/parts/comments/common/commentModel.ts b/src/vs/workbench/parts/comments/common/commentModel.ts index 1d22f17c626..8eed03dfcff 100644 --- a/src/vs/workbench/parts/comments/common/commentModel.ts +++ b/src/vs/workbench/parts/comments/common/commentModel.ts @@ -71,12 +71,12 @@ export class CommentsModel { public updateCommentThreads(event: ICommentThreadChangedEvent): boolean { const { owner, removed, changed, added } = event; - if (!this.commentThreadsMap.has(owner)) { + + const threadsForOwner = this.commentThreadsMap.get(owner); + if (!threadsForOwner) { return false; } - let threadsForOwner = this.commentThreadsMap.get(owner); - removed.forEach(thread => { // Find resource that has the comment thread const matchingResourceIndex = firstIndex(threadsForOwner, (resourceData) => resourceData.id === thread.resource); diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index e1e13bdf141..507c732f58f 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -86,7 +86,7 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb codeActionsPerMarker = new Map(); this.codeActions.set(marker.resource.toString(), codeActionsPerMarker); } - let codeActions = codeActionsPerMarker.get(markerKey); + const codeActions = codeActionsPerMarker.get(markerKey); if (codeActions) { return Promise.resolve(this.toActions(codeActions, marker)); } else { @@ -98,10 +98,10 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb if (!codeActionsPromisesPerMarker.has(markerKey)) { const codeActionsPromise = this.getFixes(marker); codeActionsPromisesPerMarker.set(markerKey, codeActionsPromise); - codeActionsPromise.then(codeActions => codeActionsPerMarker.set(markerKey, codeActions)); + codeActionsPromise.then(codeActions => codeActionsPerMarker!.set(markerKey, codeActions)); } // Wait for 100ms for code actions fetching. - return timeout(100).then(() => this.toActions(codeActionsPerMarker.get(markerKey) || [], marker)); + return timeout(100).then(() => this.toActions(codeActionsPerMarker!.get(markerKey) || [], marker)); } } diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 3c7ac102e43..524250d2cf2 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -223,8 +223,8 @@ class SnippetsService implements ISnippetsService { } const resource = validContribution.location.toString(); - if (this._files.has(resource)) { - const file = this._files.get(resource); + const file = this._files.get(resource); + if (file) { if (file.defaultScopes) { file.defaultScopes.push(validContribution.language); } else { From 93ebd40953120b0b5c01c5179fc61c545328e833 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 17:30:01 -0800 Subject: [PATCH 0128/1066] Continue Strict null check for Map.get may return undefined --- src/vs/base/browser/ui/tree/indexTreeModel.ts | 8 +++--- src/vs/base/node/ps.ts | 2 +- .../contrib/colorPicker/colorDetector.ts | 2 +- .../editor/contrib/snippet/snippetParser.ts | 5 ++-- .../editor/contrib/snippet/snippetSession.ts | 2 +- .../browser/standaloneThemeServiceImpl.ts | 4 +-- .../actions/browser/menuItemActionItem.ts | 2 +- src/vs/platform/commands/common/commands.ts | 3 ++- .../test/common/mockKeybindingService.ts | 5 ++-- .../electron-browser/lifecycleService.ts | 5 ++-- .../api/node/extHostSearch.fileIndex.ts | 6 ++--- src/vs/workbench/api/node/extHostTypes.ts | 16 ++++++------ src/vs/workbench/common/views.ts | 25 +++++++++++-------- .../workbench/electron-browser/resources.ts | 4 +-- .../parts/search/common/queryBuilder.ts | 4 +-- .../snippets/electron-browser/snippetsFile.ts | 2 +- .../cachedExtensionScanner.ts | 15 ++++++----- .../electron-browser/extensionHostProfiler.ts | 7 ++++-- .../keybinding/common/keyboardMapper.ts | 9 ++++--- 19 files changed, 70 insertions(+), 56 deletions(-) diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index fe9aa374c78..4390a4dc512 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; -import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; +import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; -import { ITreeFilterDataResult, TreeVisibility, ITreeFilter, ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; +import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; +import { ISequence, Iterator } from 'vs/base/common/iterator'; +import { ISpliceable } from 'vs/base/common/sequence'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index fd39b2ce4a2..8117be30736 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -218,7 +218,7 @@ export function listProcesses(rootPid: number): Promise { } else { const cpuUsage = stdout.toString().split('\n'); for (let i = 0; i < pids.length; i++) { - const processInfo = map.get(pids[i]); + const processInfo = map.get(pids[i])!; processInfo.load = parseFloat(cpuUsage[i]); } diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index 1ce10338932..02be8db0cae 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -242,7 +242,7 @@ export class ColorDetector implements IEditorContribution { return null; } - return this._colorDatas.get(decorations[0].id); + return this._colorDatas.get(decorations[0].id)!; } } diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index badfff931d5..d869799518a 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -626,10 +626,11 @@ export class SnippetParser { return true; }); for (const placeholder of incompletePlaceholders) { - if (placeholderDefaultValues.has(placeholder.index)) { + const defaultValues = placeholderDefaultValues.get(placeholder.index); + if (defaultValues) { const clone = new Placeholder(placeholder.index); clone.transform = placeholder.transform; - for (const child of placeholderDefaultValues.get(placeholder.index)) { + for (const child of defaultValues) { clone.appendChild(child.clone()); } snippet.replace(placeholder, [clone]); diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index dbb70edcd44..8255cc35fd8 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -145,7 +145,7 @@ export class OneSnippet { // Special case #2: placeholders enclosing active placeholders const selections: Selection[] = []; for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { - const id = this._placeholderDecorations.get(placeholder); + const id = this._placeholderDecorations.get(placeholder)!; const range = this._editor.getModel().getDecorationRange(id)!; selections.push(new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)); diff --git a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts index 50633da83e2..d46974b5dd3 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeServiceImpl.ts @@ -212,9 +212,9 @@ export class StandaloneThemeServiceImpl implements IStandaloneThemeService { public setTheme(themeName: string): string { let theme: StandaloneTheme; if (this._knownThemes.has(themeName)) { - theme = this._knownThemes.get(themeName); + theme = this._knownThemes.get(themeName)!; } else { - theme = this._knownThemes.get(VS_THEME_NAME); + theme = this._knownThemes.get(VS_THEME_NAME)!; } if (this._theme === theme) { // Nothing to do diff --git a/src/vs/platform/actions/browser/menuItemActionItem.ts b/src/vs/platform/actions/browser/menuItemActionItem.ts index 7e58a70ff1f..872b66f8e5a 100644 --- a/src/vs/platform/actions/browser/menuItemActionItem.ts +++ b/src/vs/platform/actions/browser/menuItemActionItem.ts @@ -231,7 +231,7 @@ export class MenuItemActionItem extends ActionItem { const iconPathMapKey = item.iconLocation.dark.toString(); if (MenuItemActionItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) { - iconClass = MenuItemActionItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey); + iconClass = MenuItemActionItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!; } else { iconClass = ids.nextId(); createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`); diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 528601491b2..5a5bf2ca121 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -96,7 +96,8 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR let ret = toDisposable(() => { removeFn(); - if (this._commands.get(id).isEmpty()) { + const command = this._commands.get(id); + if (command && command.isEmpty()) { this._commands.delete(id); } }); diff --git a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts index a4056ae0d71..91721a6b0cf 100644 --- a/src/vs/platform/keybinding/test/common/mockKeybindingService.ts +++ b/src/vs/platform/keybinding/test/common/mockKeybindingService.ts @@ -54,8 +54,9 @@ export class MockContextKeyService implements IContextKeyService { return Event.None; } public getContextKeyValue(key: string) { - if (this._keys.has(key)) { - return this._keys.get(key).get(); + const value = this._keys.get(key); + if (value) { + return value.get(); } } public getContext(domNode: HTMLElement): any { diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index ad8f31d24cf..b2a7a6e31b6 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -163,8 +163,9 @@ export class LifecycleService extends Disposable implements ILifecycleService { this._phase = value; mark(`LifecyclePhase/${LifecyclePhaseToString(value)}`); - if (this.phaseWhen.has(this._phase)) { - this.phaseWhen.get(this._phase).open(); + const barrier = this.phaseWhen.get(this._phase); + if (barrier) { + barrier.open(); this.phaseWhen.delete(this._phase); } } diff --git a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts index 3e61b4c56be..7e32abf71b1 100644 --- a/src/vs/workbench/api/node/extHostSearch.fileIndex.ts +++ b/src/vs/workbench/api/node/extHostSearch.fileIndex.ts @@ -347,7 +347,7 @@ export class FileIndexSearchManager { this.folderCacheKeys.set(config.cacheKey!, new Set()); } - this.folderCacheKeys.get(config.cacheKey!).add(folderCacheKey!); + this.folderCacheKeys.get(config.cacheKey!)!.add(folderCacheKey!); return folderCacheKey!; } @@ -553,11 +553,11 @@ export class FileIndexSearchManager { } public clearCache(cacheKey: string): void { - if (!this.folderCacheKeys.has(cacheKey)) { + const expandedKeys = this.folderCacheKeys.get(cacheKey); + if (!expandedKeys) { return undefined; } - const expandedKeys = this.folderCacheKeys.get(cacheKey); expandedKeys.forEach(key => delete this.caches[key]); this.folderCacheKeys.delete(cacheKey); diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 9f7d7eb99f2..90c05fd6e11 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -4,17 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import * as crypto from 'crypto'; - -import { URI } from 'vs/base/common/uri'; -import { illegalArgument } from 'vs/base/common/errors'; -import * as vscode from 'vscode'; -import { isMarkdownString } from 'vs/base/common/htmlContent'; -import { IRelativePattern } from 'vs/base/common/glob'; import { relative } from 'path'; -import { startsWith } from 'vs/base/common/strings'; -import { values } from 'vs/base/common/map'; import { coalesce, equals } from 'vs/base/common/arrays'; +import { illegalArgument } from 'vs/base/common/errors'; +import { IRelativePattern } from 'vs/base/common/glob'; +import { isMarkdownString } from 'vs/base/common/htmlContent'; +import { values } from 'vs/base/common/map'; +import { startsWith } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import * as vscode from 'vscode'; + export class Disposable { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 721071521e4..afb29fd530c 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -52,7 +52,7 @@ export interface IViewContainersRegistry { * * @returns the view container with given id. */ - get(id: string): ViewContainer; + get(id: string): ViewContainer | undefined; } export class ViewContainer { @@ -71,19 +71,22 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry { } registerViewContainer(id: string, extensionId: ExtensionIdentifier): ViewContainer { - if (!this.viewContainers.has(id)) { - const viewContainer = new class extends ViewContainer { - constructor() { - super(id, extensionId); - } - }; - this.viewContainers.set(id, viewContainer); - this._onDidRegister.fire(viewContainer); + const existing = this.viewContainers.get(id); + if (existing) { + return existing; } - return this.get(id); + + const viewContainer = new class extends ViewContainer { + constructor() { + super(id, extensionId); + } + }; + this.viewContainers.set(id, viewContainer); + this._onDidRegister.fire(viewContainer); + return viewContainer; } - get(id: string): ViewContainer { + get(id: string): ViewContainer | undefined { return this.viewContainers.get(id); } } diff --git a/src/vs/workbench/electron-browser/resources.ts b/src/vs/workbench/electron-browser/resources.ts index 8fe732e5f6c..74c2b6751e6 100644 --- a/src/vs/workbench/electron-browser/resources.ts +++ b/src/vs/workbench/electron-browser/resources.ts @@ -96,9 +96,9 @@ export class ResourceGlobMatcher extends Disposable { let expressionForRoot: ParsedExpression; if (folder && this.mapRootToParsedExpression.has(folder.uri.toString())) { - expressionForRoot = this.mapRootToParsedExpression.get(folder.uri.toString()); + expressionForRoot = this.mapRootToParsedExpression.get(folder.uri.toString())!; } else { - expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT); + expressionForRoot = this.mapRootToParsedExpression.get(ResourceGlobMatcher.NO_ROOT)!; } // If the resource if from a workspace, convert its absolute path to a relative diff --git a/src/vs/workbench/parts/search/common/queryBuilder.ts b/src/vs/workbench/parts/search/common/queryBuilder.ts index a90a18bf533..979b7a2e4d9 100644 --- a/src/vs/workbench/parts/search/common/queryBuilder.ts +++ b/src/vs/workbench/parts/search/common/queryBuilder.ts @@ -286,9 +286,9 @@ export class QueryBuilder { const searchPathPatternMap = new Map(); expandedSearchPaths.forEach(oneSearchPathPattern => { const key = oneSearchPathPattern.searchPath.toString(); - if (searchPathPatternMap.has(key)) { + const existing = searchPathPatternMap.get(key); + if (existing) { if (oneSearchPathPattern.pattern) { - const existing = searchPathPatternMap.get(key); existing.pattern = existing.pattern || {}; existing.pattern[oneSearchPathPattern.pattern] = true; } diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts index e09956dae4e..4dae6d5faa8 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsFile.ts @@ -94,7 +94,7 @@ export class Snippet { // a 'variable' without a default value and not being one of our supported // variables is automatically turned into a placeholder. This is to restore // a bug we had before. So `${foo}` becomes `${N:foo}` - const index = placeholders.has(marker.name) ? placeholders.get(marker.name) : ++placeholderMax; + const index = placeholders.has(marker.name) ? placeholders.get(marker.name)! : ++placeholderMax; placeholders.set(marker.name, index); const synthetic = new Placeholder(index).appendChild(new Text(marker.name)); diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index af7ac0d6b9f..ed093e088d9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -69,23 +69,26 @@ export class CachedExtensionScanner { let result = new Map(); system.forEach((systemExtension) => { const extensionKey = ExtensionIdentifier.toKey(systemExtension.identifier); - if (result.has(extensionKey)) { - log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, systemExtension.extensionLocation.fsPath)); + const extension = result.get(extensionKey); + if (extension) { + log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, systemExtension.extensionLocation.fsPath)); } result.set(extensionKey, systemExtension); }); user.forEach((userExtension) => { const extensionKey = ExtensionIdentifier.toKey(userExtension.identifier); - if (result.has(extensionKey)) { - log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); + const extension = result.get(extensionKey); + if (extension) { + log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); } result.set(extensionKey, userExtension); }); development.forEach(developedExtension => { log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); const extensionKey = ExtensionIdentifier.toKey(developedExtension.identifier); - if (result.has(extensionKey)) { - log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", result.get(extensionKey).extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); + const extension = result.get(extensionKey); + if (extension) { + log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); } result.set(extensionKey, developedExtension); }); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 3cc306bd9d8..852b3130a2e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -62,8 +62,11 @@ export class ExtensionHostProfiler { idsToSegmentId.set(node.id, segmentId); if (node.children) { - for (let child of node.children) { - visit(idsToNodes.get(child), segmentId); + for (const child of node.children) { + const childNode = idsToNodes.get(child); + if (childNode) { + visit(childNode, segmentId); + } } } } diff --git a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts index fc486810516..36c9f6ca888 100644 --- a/src/vs/workbench/services/keybinding/common/keyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/keyboardMapper.ts @@ -29,13 +29,14 @@ export class CachedKeyboardMapper implements IKeyboardMapper { } public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] { - let hashCode = keybinding.getHashCode(); - if (!this._cache.has(hashCode)) { - let r = this._actual.resolveKeybinding(keybinding); + const hashCode = keybinding.getHashCode(); + const resolved = this._cache.get(hashCode); + if (!resolved) { + const r = this._actual.resolveKeybinding(keybinding); this._cache.set(hashCode, r); return r; } - return this._cache.get(hashCode); + return resolved; } public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { From e325cce9bc3a7f3c1883dbd7c7eab31049b526db Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 18:15:34 -0800 Subject: [PATCH 0129/1066] Fixing markdown unit tests --- .../src/markdownEngine.ts | 57 ++++++++++--------- .../src/tableOfContentsProvider.ts | 1 + .../src/test/inMemoryDocument.ts | 4 +- .../src/test/workspaceSymbolProvider.test.ts | 4 +- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index e66c64423df..c77358048bf 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -13,16 +13,19 @@ import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; +const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; + export class MarkdownEngine { private md?: MarkdownIt; private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map(); - private cachedTokens = new Map(); - - - + private _cache?: { + readonly document: vscode.Uri; + readonly version: number; + readonly tokens: Token[] + }; public constructor( private readonly extensionPreviewResourceProvider: MarkdownContributions, @@ -86,24 +89,32 @@ export class MarkdownEngine { return this.md; } - private stripFrontmatter(text: string): { text: string, offset: number } { + private stripFrontmatter(text: string): { frontMatter?: string, body: string, lineOffset: number } { let offset = 0; const frontMatterMatch = FrontMatterRegex.exec(text); + let frontMatter: string | undefined; if (frontMatterMatch) { - const frontMatter = frontMatterMatch[0]; + frontMatter = frontMatterMatch[0]; offset = frontMatter.split(/\r\n|\n|\r/g).length - 1; text = text.substr(frontMatter.length); } - return { text, offset }; + return { frontMatter, body: text, lineOffset: offset }; } private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { - const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; - const { text, offset } = this.stripFrontmatter(document.getText()); + const { body: text, lineOffset: offset } = this.stripFrontmatter(document.getText()); const uri = document.uri; - if (this.cachedTokens.has(uri)) { - return this.cachedTokens.get(uri)!; + if (this._cache + && this._cache.document.toString() === uri.toString() + && this._cache.version === document.version + ) { + return this._cache.tokens; } + + this.currentDocument = document.uri; + this._slugCount = new Map(); + this.firstLine = offset; + const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { if (token.map) { token.map[0] += offset; @@ -111,29 +122,21 @@ export class MarkdownEngine { } return token; }); - this.cachedTokens.set(uri, tokens); + this._cache = { + tokens, + document: uri, + version: document.version + }; return tokens; } - public async render(document: SkinnyTextDocument, stripFrontmatter: boolean): Promise { - let offset = 0; - let text = document.getText(); - if (stripFrontmatter) { - const markdownContent = this.stripFrontmatter(text); - offset = markdownContent.offset; - text = markdownContent.text; - } - this.currentDocument = document.uri; - this.firstLine = offset; - this._slugCount = new Map(); - + public async render(document: SkinnyTextDocument, _stripFrontmatter: boolean): Promise { const engine = await this.getEngine(document.uri); - return engine.renderer.render(this.tokenize(document, engine), this.md, {}); + const html = engine.renderer.render(this.tokenize(document, engine), this.md, {}); + return html; } public async parse(document: SkinnyTextDocument): Promise { - this.currentDocument = document.uri; - this._slugCount = new Map(); const engine = await this.getEngine(document.uri); return this.tokenize(document, engine); } diff --git a/extensions/markdown-language-features/src/tableOfContentsProvider.ts b/extensions/markdown-language-features/src/tableOfContentsProvider.ts index d2b041c2523..342d71f1cfe 100644 --- a/extensions/markdown-language-features/src/tableOfContentsProvider.ts +++ b/extensions/markdown-language-features/src/tableOfContentsProvider.ts @@ -17,6 +17,7 @@ export interface TocEntry { export interface SkinnyTextDocument { readonly uri: vscode.Uri; + readonly version: number; readonly lineCount: number; getText(): string; lineAt(line: number): vscode.TextLine; diff --git a/extensions/markdown-language-features/src/test/inMemoryDocument.ts b/extensions/markdown-language-features/src/test/inMemoryDocument.ts index bfcf3721b65..c2472e5a4ec 100644 --- a/extensions/markdown-language-features/src/test/inMemoryDocument.ts +++ b/extensions/markdown-language-features/src/test/inMemoryDocument.ts @@ -10,7 +10,8 @@ export class InMemoryDocument implements vscode.TextDocument { constructor( public readonly uri: vscode.Uri, - private readonly _contents: string + private readonly _contents: string, + public readonly version = 1, ) { this._lines = this._contents.split(/\n/g); } @@ -18,7 +19,6 @@ export class InMemoryDocument implements vscode.TextDocument { isUntitled: boolean = false; languageId: string = ''; - version: number = 1; isDirty: boolean = false; isClosed: boolean = false; eol: vscode.EndOfLine = vscode.EndOfLine.LF; diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index f5b3eb0ea4a..d19b6611699 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -52,7 +52,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { const testFileName = vscode.Uri.file('test.md'); const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([ - new InMemoryDocument(testFileName, `# header1`) + new InMemoryDocument(testFileName, `# header1`, 1 /* version */) ]); const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); @@ -60,7 +60,7 @@ suite('markdown.WorkspaceSymbolProvider', () => { assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`)); + workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); const newSymbols = await provider.provideWorkspaceSymbols(''); assert.strictEqual(newSymbols.length, 2); assert.strictEqual(newSymbols[0].name, '# new header'); From 1bbc0e3b4dcdcdba4ae8e897e43059bcb90e7db9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 19:05:55 -0800 Subject: [PATCH 0130/1066] Deprecate the previewFrontMatter setting Switch to always hiding the frontmatter and then allowing markdown-it plugins to render it properly if they wish. `previewFrontMatter: "show"` is also not very useful since it usually results in a jumble of text at the top of the file This is required with the new performance work to avoid re-tokenizing the document multiple times during rendering --- .../markdown-language-features/package.json | 11 +---- .../package.nls.json | 1 - .../src/features/previewConfig.ts | 2 - .../src/features/previewContentProvider.ts | 2 +- .../src/markdownEngine.ts | 47 ++++++++----------- .../markdown-language-features/yarn.lock | 5 ++ 6 files changed, 27 insertions(+), 41 deletions(-) diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 8c65847f4df..2e13c9aaa38 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -181,16 +181,6 @@ "description": "%markdown.styles.dec%", "scope": "resource" }, - "markdown.previewFrontMatter": { - "type": "string", - "enum": [ - "hide", - "show" - ], - "default": "hide", - "description": "%markdown.previewFrontMatter.dec%", - "scope": "resource" - }, "markdown.preview.breaks": { "type": "boolean", "default": false, @@ -307,6 +297,7 @@ "dependencies": { "highlight.js": "9.13.1", "markdown-it": "^8.4.2", + "markdown-it-front-matter": "^0.1.2", "vscode-extension-telemetry": "0.1.0", "vscode-nls": "^4.0.0" }, diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index ca3d7226f58..7841caff810 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -13,7 +13,6 @@ "markdown.preview.scrollPreviewWithEditorSelection.desc": "[Deprecated] Scrolls the markdown preview to reveal the currently selected line from the editor.", "markdown.preview.scrollPreviewWithEditorSelection.deprecationMessage": "This setting has been replaced by 'markdown.preview.scrollPreviewWithEditor' and no longer has any effect.", "markdown.preview.title": "Open Preview", - "markdown.previewFrontMatter.dec": "Sets how YAML front matter should be rendered in the markdown preview. 'hide' removes the front matter. Otherwise, the front matter is treated as markdown content.", "markdown.previewSide.title": "Open Preview to the Side", "markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side", "markdown.showSource.title": "Show Source", diff --git a/extensions/markdown-language-features/src/features/previewConfig.ts b/extensions/markdown-language-features/src/features/previewConfig.ts index d660bf10793..ed09a9ce46e 100644 --- a/extensions/markdown-language-features/src/features/previewConfig.ts +++ b/extensions/markdown-language-features/src/features/previewConfig.ts @@ -12,7 +12,6 @@ export class MarkdownPreviewConfiguration { public readonly scrollBeyondLastLine: boolean; public readonly wordWrap: boolean; - public readonly previewFrontMatter: string; public readonly lineBreaks: boolean; public readonly doubleClickToSwitchToEditor: boolean; public readonly scrollEditorWithPreview: boolean; @@ -36,7 +35,6 @@ export class MarkdownPreviewConfiguration { this.wordWrap = markdownEditorConfig['editor.wordWrap'] !== 'off'; } - this.previewFrontMatter = markdownConfig.get('previewFrontMatter', 'hide'); this.scrollPreviewWithEditor = !!markdownConfig.get('preview.scrollPreviewWithEditor', true); this.scrollEditorWithPreview = !!markdownConfig.get('preview.scrollEditorWithPreview', true); this.lineBreaks = !!markdownConfig.get('preview.breaks', false); diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index 403fc9249d8..c5efa00ac3c 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -68,7 +68,7 @@ export class MarkdownContentProvider { const nonce = new Date().getTime() + '' + new Date().getMilliseconds(); const csp = this.getCspForResource(sourceUri, nonce); - const body = await this.engine.render(markdownDocument, config.previewFrontMatter === 'hide'); + const body = await this.engine.render(markdownDocument); return ` diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index c77358048bf..9195507a620 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -12,13 +12,11 @@ import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; -const FrontMatterRegex = /^---\s*[^]*?(-{3}|\.{3})\s*/; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; export class MarkdownEngine { private md?: MarkdownIt; - private firstLine?: number; private currentDocument?: vscode.Uri; private _slugCount = new Map(); private _cache?: { @@ -69,6 +67,21 @@ export class MarkdownEngine { this.usePlugin(await plugin); } + const frontMatterPlugin = require('markdown-it-front-matter'); + // Extract rules from front matter plugin and apply at a lower precedence + let fontMatterRule: any; + frontMatterPlugin({ + block: { + ruler: { + before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } + } + } + }, () => { /* noop */ }); + + this.md.block.ruler.before('fence', 'front_matter', fontMatterRule, { + alt: ['paragraph', 'reference', 'blockquote', 'list'] + }); + for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { this.addLineNumberRenderer(this.md, renderName); } @@ -89,20 +102,7 @@ export class MarkdownEngine { return this.md; } - private stripFrontmatter(text: string): { frontMatter?: string, body: string, lineOffset: number } { - let offset = 0; - const frontMatterMatch = FrontMatterRegex.exec(text); - let frontMatter: string | undefined; - if (frontMatterMatch) { - frontMatter = frontMatterMatch[0]; - offset = frontMatter.split(/\r\n|\n|\r/g).length - 1; - text = text.substr(frontMatter.length); - } - return { frontMatter, body: text, lineOffset: offset }; - } - private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { - const { body: text, lineOffset: offset } = this.stripFrontmatter(document.getText()); const uri = document.uri; if (this._cache && this._cache.document.toString() === uri.toString() @@ -113,15 +113,9 @@ export class MarkdownEngine { this.currentDocument = document.uri; this._slugCount = new Map(); - this.firstLine = offset; - const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}).map(token => { - if (token.map) { - token.map[0] += offset; - token.map[1] += offset; - } - return token; - }); + const text = document.getText(); + const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); this._cache = { tokens, document: uri, @@ -130,10 +124,9 @@ export class MarkdownEngine { return tokens; } - public async render(document: SkinnyTextDocument, _stripFrontmatter: boolean): Promise { + public async render(document: SkinnyTextDocument): Promise { const engine = await this.getEngine(document.uri); - const html = engine.renderer.render(this.tokenize(document, engine), this.md, {}); - return html; + return engine.renderer.render(this.tokenize(document, engine), engine, {}); } public async parse(document: SkinnyTextDocument): Promise { @@ -146,7 +139,7 @@ export class MarkdownEngine { md.renderer.rules[ruleName] = (tokens: any, idx: number, options: any, env: any, self: any) => { const token = tokens[idx]; if (token.map && token.map.length) { - token.attrSet('data-line', this.firstLine + token.map[0]); + token.attrSet('data-line', token.map[0]); token.attrJoin('class', 'code-line'); } diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 071b5b8e161..af3977a75cc 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -3895,6 +3895,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-it-front-matter@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20" + integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA= + markdown-it@^8.4.2: version "8.4.2" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" From 5496ba76173454f4c025aeef20fa613db74007b2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 19:10:47 -0800 Subject: [PATCH 0131/1066] Prevent possible race while initilizing markdown engine --- .../src/markdownEngine.ts | 119 +++++++++--------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 9195507a620..4c1fbfe9845 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -15,7 +15,7 @@ import { getUriForLinkWithKnownExternalScheme } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; export class MarkdownEngine { - private md?: MarkdownIt; + private md?: Promise; private currentDocument?: vscode.Uri; private _slugCount = new Map(); @@ -30,82 +30,81 @@ export class MarkdownEngine { private readonly slugifier: Slugifier, ) { } - private usePlugin(factory: (md: any) => any): void { - try { - this.md = factory(this.md); - } catch (e) { - // noop - } - } - private async getEngine(resource: vscode.Uri): Promise { if (!this.md) { - const hljs = await import('highlight.js'); - this.md = (await import('markdown-it'))({ - html: true, - highlight: (str: string, lang?: string) => { - // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { - lang = 'jsx'; + this.md = import('markdown-it').then(async markdownIt => { + const hljs = await import('highlight.js'); + let md = markdownIt({ + html: true, + highlight: (str: string, lang?: string) => { + // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 + if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { + lang = 'jsx'; + } + if (lang && lang.toLocaleLowerCase() === 'json5') { + lang = 'json'; + } + if (lang && lang.toLocaleLowerCase() === 'c#') { + lang = 'cs'; + } + if (lang && hljs.getLanguage(lang)) { + try { + return `
${hljs.highlight(lang, str, true).value}
`; + } catch (error) { } + } + return `
${md!.utils.escapeHtml(str)}
`; } - if (lang && lang.toLocaleLowerCase() === 'json5') { - lang = 'json'; - } - if (lang && lang.toLocaleLowerCase() === 'c#') { - lang = 'cs'; - } - if (lang && hljs.getLanguage(lang)) { - try { - return `
${hljs.highlight(lang, str, true).value}
`; - } catch (error) { } - } - return `
${this.md!.utils.escapeHtml(str)}
`; - } - }); + }); - for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { - this.usePlugin(await plugin); - } - - const frontMatterPlugin = require('markdown-it-front-matter'); - // Extract rules from front matter plugin and apply at a lower precedence - let fontMatterRule: any; - frontMatterPlugin({ - block: { - ruler: { - before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } + for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { + try { + md = (await plugin)(md); + } catch { + // noop } } - }, () => { /* noop */ }); - this.md.block.ruler.before('fence', 'front_matter', fontMatterRule, { - alt: ['paragraph', 'reference', 'blockquote', 'list'] + const frontMatterPlugin = require('markdown-it-front-matter'); + // Extract rules from front matter plugin and apply at a lower precedence + let fontMatterRule: any; + frontMatterPlugin({ + block: { + ruler: { + before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } + } + } + }, () => { /* noop */ }); + + md.block.ruler.before('fence', 'front_matter', fontMatterRule, { + alt: ['paragraph', 'reference', 'blockquote', 'list'] + }); + + for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { + this.addLineNumberRenderer(md, renderName); + } + + this.addImageStabilizer(md); + this.addFencedRenderer(md); + + this.addLinkNormalizer(md); + this.addLinkValidator(md); + this.addNamedHeaders(md); + return md; }); - - for (const renderName of ['paragraph_open', 'heading_open', 'image', 'code_block', 'fence', 'blockquote_open', 'list_item_open']) { - this.addLineNumberRenderer(this.md, renderName); - } - - this.addImageStabilizer(this.md); - this.addFencedRenderer(this.md); - - this.addLinkNormalizer(this.md); - this.addLinkValidator(this.md); - this.addNamedHeaders(this.md); } + const md = await this.md!; const config = vscode.workspace.getConfiguration('markdown', resource); - this.md.set({ + md.set({ breaks: config.get('preview.breaks', false), linkify: config.get('preview.linkify', true) }); - return this.md; + return md; } private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { - const uri = document.uri; if (this._cache - && this._cache.document.toString() === uri.toString() + && this._cache.document.toString() === document.uri.toString() && this._cache.version === document.version ) { return this._cache.tokens; @@ -118,7 +117,7 @@ export class MarkdownEngine { const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); this._cache = { tokens, - document: uri, + document: document.uri, version: document.version }; return tokens; From 758280cc474fa6a360074092cd8b4a9190247b47 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 19:27:41 -0800 Subject: [PATCH 0132/1066] Extract TokenCache --- .../src/markdownEngine.ts | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 4c1fbfe9845..4d38173efe5 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -14,16 +14,38 @@ import { getUriForLinkWithKnownExternalScheme } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; +class TokenCache { + private cachedDocument?: { + readonly uri: vscode.Uri; + readonly version: number; + }; + private tokens?: Token[]; + + public tryGetCached(document: SkinnyTextDocument): Token[] | undefined { + if (this.cachedDocument + && this.cachedDocument.uri.toString() === document.uri.toString() + && this.cachedDocument.version === document.version + ) { + return this.tokens; + } + return undefined; + } + + public update(document: SkinnyTextDocument, tokens: Token[]) { + this.cachedDocument = { + uri: document.uri, + version: document.version + }; + this.tokens = tokens; + } +} + export class MarkdownEngine { private md?: Promise; private currentDocument?: vscode.Uri; private _slugCount = new Map(); - private _cache?: { - readonly document: vscode.Uri; - readonly version: number; - readonly tokens: Token[] - }; + private _tokenCache = new TokenCache(); public constructor( private readonly extensionPreviewResourceProvider: MarkdownContributions, @@ -103,11 +125,9 @@ export class MarkdownEngine { } private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { - if (this._cache - && this._cache.document.toString() === document.uri.toString() - && this._cache.version === document.version - ) { - return this._cache.tokens; + const cached = this._tokenCache.tryGetCached(document); + if (cached) { + return cached; } this.currentDocument = document.uri; @@ -115,11 +135,7 @@ export class MarkdownEngine { const text = document.getText(); const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); - this._cache = { - tokens, - document: document.uri, - version: document.version - }; + this._tokenCache.update(document, tokens); return tokens; } From 344f4fa361e098b49aeace9602e89275fbbd17bf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 9 Jan 2019 19:32:53 -0800 Subject: [PATCH 0133/1066] Make sure we update the markdown document on config changes --- .../src/markdownEngine.ts | 51 +++++++++++++------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 4d38173efe5..a9fd3a1ab8a 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -14,27 +14,36 @@ import { getUriForLinkWithKnownExternalScheme } from './util/links'; const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g; +interface MarkdownItConfig { + readonly breaks: boolean; + readonly linkify: boolean; +} + class TokenCache { private cachedDocument?: { readonly uri: vscode.Uri; readonly version: number; + readonly config: MarkdownItConfig; }; private tokens?: Token[]; - public tryGetCached(document: SkinnyTextDocument): Token[] | undefined { + public tryGetCached(document: SkinnyTextDocument, config: MarkdownItConfig): Token[] | undefined { if (this.cachedDocument && this.cachedDocument.uri.toString() === document.uri.toString() && this.cachedDocument.version === document.version + && this.cachedDocument.config.breaks === config.breaks + && this.cachedDocument.config.linkify === config.linkify ) { return this.tokens; } return undefined; } - public update(document: SkinnyTextDocument, tokens: Token[]) { + public update(document: SkinnyTextDocument, config: MarkdownItConfig, tokens: Token[]) { this.cachedDocument = { uri: document.uri, - version: document.version + version: document.version, + config, }; this.tokens = tokens; } @@ -52,7 +61,7 @@ export class MarkdownEngine { private readonly slugifier: Slugifier, ) { } - private async getEngine(resource: vscode.Uri): Promise { + private async getEngine(config: MarkdownItConfig): Promise { if (!this.md) { this.md = import('markdown-it').then(async markdownIt => { const hljs = await import('highlight.js'); @@ -116,16 +125,16 @@ export class MarkdownEngine { } const md = await this.md!; - const config = vscode.workspace.getConfiguration('markdown', resource); - md.set({ - breaks: config.get('preview.breaks', false), - linkify: config.get('preview.linkify', true) - }); + md.set(config); return md; } - private tokenize(document: SkinnyTextDocument, engine: MarkdownIt): Token[] { - const cached = this._tokenCache.tryGetCached(document); + private tokenize( + document: SkinnyTextDocument, + config: MarkdownItConfig, + engine: MarkdownIt + ): Token[] { + const cached = this._tokenCache.tryGetCached(document, config); if (cached) { return cached; } @@ -135,18 +144,28 @@ export class MarkdownEngine { const text = document.getText(); const tokens = engine.parse(text.replace(UNICODE_NEWLINE_REGEX, ''), {}); - this._tokenCache.update(document, tokens); + this._tokenCache.update(document, config, tokens); return tokens; } public async render(document: SkinnyTextDocument): Promise { - const engine = await this.getEngine(document.uri); - return engine.renderer.render(this.tokenize(document, engine), engine, {}); + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return engine.renderer.render(this.tokenize(document, config, engine), engine, {}); } public async parse(document: SkinnyTextDocument): Promise { - const engine = await this.getEngine(document.uri); - return this.tokenize(document, engine); + const config = this.getConfig(document.uri); + const engine = await this.getEngine(config); + return this.tokenize(document, config, engine); + } + + private getConfig(resource: vscode.Uri): MarkdownItConfig { + const config = vscode.workspace.getConfiguration('markdown', resource); + return { + breaks: config.get('preview.breaks', false), + linkify: config.get('preview.linkify', true) + }; } private addLineNumberRenderer(md: any, ruleName: string): void { From b679274562347203f5185fd0dd2f80bea88b51f3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 10 Jan 2019 08:29:34 +0100 Subject: [PATCH 0134/1066] Fixes Microsoft/monaco-editor#1255: Treat WKWebView the same as Safari around user-select --- src/vs/base/browser/browser.ts | 3 ++- src/vs/editor/browser/config/configuration.ts | 11 +++-------- src/vs/editor/browser/viewParts/lines/viewLines.css | 13 +++---------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index e687780e57c..639ac3d031f 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -146,7 +146,8 @@ export const isOpera = (userAgent.indexOf('Opera') >= 0); export const isFirefox = (userAgent.indexOf('Firefox') >= 0); export const isWebKit = (userAgent.indexOf('AppleWebKit') >= 0); export const isChrome = (userAgent.indexOf('Chrome') >= 0); -export const isSafari = (userAgent.indexOf('Chrome') === -1) && (userAgent.indexOf('Safari') >= 0); +export const isSafari = (!isChrome && (userAgent.indexOf('Safari') >= 0)); +export const isWebkitWebView = (!isChrome && !isSafari && isWebKit); export const isIPad = (userAgent.indexOf('iPad') >= 0); export const isEdgeWebView = isEdge && (userAgent.indexOf('WebView/') >= 0); diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index d22f4152474..cfdce8929b2 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -345,14 +345,9 @@ export class Configuration extends CommonEditorConfiguration { private _getExtraEditorClassName(): string { let extra = ''; - if (browser.isIE) { - extra += 'ie '; - } else if (browser.isFirefox) { - extra += 'ff '; - } else if (browser.isEdge) { - extra += 'edge '; - } else if (browser.isSafari) { - extra += 'safari '; + if (!browser.isSafari && !browser.isWebkitWebView) { + // Use user-select: none in all browsers except Safari and native macOS WebView + extra += 'no-user-select '; } if (platform.isMacintosh) { extra += 'mac '; diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.css b/src/vs/editor/browser/viewParts/lines/viewLines.css index f1a518d871f..fee7f696501 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.css +++ b/src/vs/editor/browser/viewParts/lines/viewLines.css @@ -14,16 +14,9 @@ 100% { background-color: none } }*/ -.monaco-editor.safari .lines-content, -.monaco-editor.safari .view-line, -.monaco-editor.safari .view-lines { - -webkit-user-select: text; - user-select: text; -} - -.monaco-editor .lines-content, -.monaco-editor .view-line, -.monaco-editor .view-lines { +.monaco-editor.no-user-select .lines-content, +.monaco-editor.no-user-select .view-line, +.monaco-editor.no-user-select .view-lines { -webkit-user-select: none; -ms-user-select: none; -khtml-user-select: none; From ed0057fd2642d53e135a113c5cae2ed18b35459c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Jan 2019 08:47:35 +0100 Subject: [PATCH 0135/1066] storage - have a connection object --- src/vs/base/node/storage.ts | 191 ++++++++++++++++++------------------ 1 file changed, 96 insertions(+), 95 deletions(-) diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 0ef368833a7..99dc420236d 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -298,9 +298,13 @@ export class Storage extends Disposable implements IStorage { } } -interface IOpenDatabaseResult { +interface IDatabaseConnection { db: Database; - path: string; + + isInMemory: boolean; + + isCorrupted?: boolean; + isErroneous?: boolean; } export interface ISQLiteStorageDatabaseOptions { @@ -322,25 +326,27 @@ export class SQLiteStorageDatabase implements IStorageDatabase { private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private path: string; private name: string; + private logger: SQLiteStorageDatabaseLogger; - private isCorrupt: boolean; - - private whenOpened: Promise; + private whenConnected: Promise; constructor(path: string, options: ISQLiteStorageDatabaseOptions = Object.create(null)) { + this.path = path; this.name = basename(path); + this.logger = new SQLiteStorageDatabaseLogger(options.logging); - this.whenOpened = this.open(path); + this.whenConnected = this.connect(path); } getItems(): Promise> { - return this.whenOpened.then(({ db }) => { + return this.whenConnected.then(connection => { const items = new Map(); - return this.all(db, 'SELECT * FROM ItemTable').then(rows => { + return this.all(connection, 'SELECT * FROM ItemTable').then(rows => { rows.forEach(row => items.set(row.key, row.value)); if (this.logger.isTracing) { @@ -369,10 +375,10 @@ export class SQLiteStorageDatabase implements IStorageDatabase { this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } - return this.whenOpened.then(({ db }) => { - return this.transaction(db, () => { + return this.whenConnected.then(connection => { + return this.transaction(connection, () => { if (request.insert && request.insert.size > 0) { - this.prepare(db, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { + this.prepare(connection, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { request.insert!.forEach((value, key) => { stmt.run([key, value]); }); @@ -389,7 +395,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } if (request.delete && request.delete.size) { - this.prepare(db, 'DELETE FROM ItemTable WHERE key=?', stmt => { + this.prepare(connection, 'DELETE FROM ItemTable WHERE key=?', stmt => { request.delete!.forEach(key => { stmt.run(key); }); @@ -409,25 +415,25 @@ export class SQLiteStorageDatabase implements IStorageDatabase { close(): Promise { this.logger.trace(`[storage ${this.name}] close()`); - return this.whenOpened.then(result => { + return this.whenConnected.then(connection => { return new Promise((resolve, reject) => { - result.db.close(closeError => { + connection.db.close(closeError => { if (closeError) { - this.handleSQLiteError(closeError, `[storage ${this.name}] close(): ${closeError}`); + this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); } - if (result.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { + if (connection.isInMemory) { return resolve(); // return early for in-memory DBs } - if (this.isCorrupt) { - // If the DB is corrupt, make sure to rename the file so that we can start - // from a fresh DB or a previous backup on the next startup and not be stuck - // with a corrupt DB for ever. + // If the DB is corrupt, make sure to rename the file so that we can start + // from a fresh DB or a previous backup on the next startup and not be stuck + // with a corrupt DB for ever. + if (connection.isCorrupted) { this.logger.error(`[storage ${this.name}] close(): removing corrupt DB and trying to restore backup`); - return always(rename(result.path, this.toCorruptPath(result.path)) - .then(() => rename(this.toBackupPath(result.path), result.path)), () => closeError ? reject(closeError) : resolve()); + return always(rename(this.path, this.toCorruptPath(this.path)) + .then(() => rename(this.toBackupPath(this.path), this.path)), () => closeError ? reject(closeError) : resolve()); } if (closeError) { @@ -438,7 +444,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // and the DB did not get corrupted during runtime, make a backup // of the DB so that we can use it as fallback in case the actual // DB becomes corrupt. - return this.backup(result).then(resolve, error => { + return this.backup(connection).then(resolve, error => { this.logger.error(`[storage ${this.name}] backup(): ${error}`); return resolve(); // ignore failing backup @@ -448,14 +454,14 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private backup(db: IOpenDatabaseResult): Promise { - if (db.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { + private backup(db: IDatabaseConnection): Promise { + if (db.isInMemory) { return Promise.resolve(); // no backups when running in-memory } - const backupPath = this.toBackupPath(db.path); + const backupPath = this.toBackupPath(this.path); - return copy(db.path, backupPath); + return copy(this.path, backupPath); } private toBackupPath(path: string): string { @@ -465,26 +471,26 @@ export class SQLiteStorageDatabase implements IStorageDatabase { checkIntegrity(full: boolean): Promise { this.logger.trace(`[storage ${this.name}] checkIntegrity(full: ${full})`); - return this.whenOpened.then(({ db }) => { - return this.get(db, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { + return this.whenConnected.then(connection => { + return this.get(connection, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { return full ? row['integrity_check'] : row['quick_check']; }); }); } - private open(path: string): Promise { + private connect(path: string): Promise { this.logger.trace(`[storage ${this.name}] open()`); return new Promise((resolve, reject) => { const fallbackToInMemoryDatabase = (error: Error) => { - this.handleSQLiteError(error, `[storage ${this.name}] open(): Error (open DB): ${error}. Falling back to in-memory DB`); + this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}. Falling back to in-memory DB`); // In case of any error to open the DB, use an in-memory // DB so that we always have a valid DB to talk to. - this.doOpen(SQLiteStorageDatabase.IN_MEMORY_PATH).then(resolve, reject); + this.doConnect(SQLiteStorageDatabase.IN_MEMORY_PATH).then(resolve, reject); }; - this.doOpen(path).then(resolve, error => { + return this.doConnect(path).then(resolve, error => { // This error code should only arise if another process is locking the same DB we // want to open at that time. This typically never happens because a DB connection @@ -492,13 +498,23 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // that the previous connection was not properly closed while the new connection is // already established. if (error.code === 'SQLITE_BUSY') { - return this.handleSQLiteBusy(path, error).then(resolve, fallbackToInMemoryDatabase); + this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); + + // Retry after some time if the DB is busy + return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.doConnect(path)).then(resolve, fallbackToInMemoryDatabase); } // This error code indicates that even though the DB file exists, // SQLite cannot open it and signals it is corrupt or not a DB. if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - return this.handleSQLiteCorrupt(path, error).then(resolve, fallbackToInMemoryDatabase); + this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error.code}`); + + // Move corrupt DB to a different filename and try to load from backup + // If that fails, a new empty DB is being created automatically + return rename(path, this.toCorruptPath(path)) + .then(() => renameIgnoreError(this.toBackupPath(path), path)) + .then(() => this.doConnect(path)) + .then(resolve, fallbackToInMemoryDatabase); } // Otherwise give up and fallback to in-memory DB @@ -507,26 +523,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private handleSQLiteBusy(path: string, error: Error & { code?: string }): Promise { - this.handleSQLiteError(error, `[storage ${this.name}] open(): Retrying after ${SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); + private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void { + connection.isErroneous = true; - // Retry after some time if the DB is busy - return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.doOpen(path)); - } - - private handleSQLiteCorrupt(path: string, error: Error & { code?: string }): Promise { - this.handleSQLiteError(error, `[storage ${this.name}] open(): Unable to open DB due to ${error.code}`); - - // Move corrupt DB to a different filename and try to load from backup - // If that fails, a new empty DB is being created automatically - return rename(path, this.toCorruptPath(path)) - .then(() => renameIgnoreError(this.toBackupPath(path), path)) - .then(() => this.doOpen(path)); - } - - private handleSQLiteError(error: Error & { code?: string }, msg: string): void { if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - this.isCorrupt = true; + connection.isCorrupted = true; } this.logger.error(msg); @@ -538,10 +539,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return `${path}.${randomSuffix}.corrupt`; } - private doOpen(path: string): Promise { - - // Reset flags when we open a DB - this.isCorrupt = false; + private doConnect(path: string): Promise { // TODO@Ben clean up performance markers return new Promise((resolve, reject) => { @@ -557,45 +555,48 @@ export class SQLiteStorageDatabase implements IStorageDatabase { mark('didRequireSQLite'); } - const db: Database = new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { - if (error) { - return db ? db.close(() => reject(error)) : reject(error); - } + const connection: IDatabaseConnection = { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + if (error) { + return connection.db ? connection.db.close(() => reject(error)) : reject(error); + } - // The following exec() statement serves two purposes: - // - create the DB if it does not exist yet - // - validate that the DB is not corrupt (the open() call does not throw otherwise) - mark('willSetupSQLiteSchema'); - this.exec(db, [ - 'PRAGMA user_version = 1;', - 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' - ].join('')).then(() => { - mark('didSetupSQLiteSchema'); + // The following exec() statement serves two purposes: + // - create the DB if it does not exist yet + // - validate that the DB is not corrupt (the open() call does not throw otherwise) + mark('willSetupSQLiteSchema'); + this.exec(connection, [ + 'PRAGMA user_version = 1;', + 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' + ].join('')).then(() => { + mark('didSetupSQLiteSchema'); - return resolve({ path, db }); - }, error => { - mark('didSetupSQLiteSchema'); + return resolve(connection); + }, error => { + mark('didSetupSQLiteSchema'); - return db.close(() => reject(error)); - }); - }); + return connection.db.close(() => reject(error)); + }); + }), + isInMemory: path === SQLiteStorageDatabase.IN_MEMORY_PATH + }; // Errors - db.on('error', error => this.handleSQLiteError(error, `[storage ${this.name}] Error (event): ${error}`)); + connection.db.on('error', error => this.handleSQLiteError(connection, error, `[storage ${this.name}] Error (event): ${error}`)); // Tracing if (this.logger.isTracing) { - db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); + connection.db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } }); }); } - private exec(db: Database, sql: string): Promise { + private exec(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.exec(sql, error => { + connection.db.exec(sql, error => { if (error) { - this.handleSQLiteError(error, `[storage ${this.name}] exec(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] exec(): ${error}`); return reject(error); } @@ -605,11 +606,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private get(db: Database, sql: string): Promise { + private get(connection: IDatabaseConnection, sql: string): Promise { return new Promise((resolve, reject) => { - db.get(sql, (error, row) => { + connection.db.get(sql, (error, row) => { if (error) { - this.handleSQLiteError(error, `[storage ${this.name}] get(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] get(): ${error}`); return reject(error); } @@ -619,11 +620,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private all(db: Database, sql: string): Promise<{ key: string, value: string }[]> { + private all(connection: IDatabaseConnection, sql: string): Promise<{ key: string, value: string }[]> { return new Promise((resolve, reject) => { - db.all(sql, (error, rows) => { + connection.db.all(sql, (error, rows) => { if (error) { - this.handleSQLiteError(error, `[storage ${this.name}] all(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] all(): ${error}`); return reject(error); } @@ -633,16 +634,16 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private transaction(db: Database, transactions: () => void): Promise { + private transaction(connection: IDatabaseConnection, transactions: () => void): Promise { return new Promise((resolve, reject) => { - db.serialize(() => { - db.run('BEGIN TRANSACTION'); + connection.db.serialize(() => { + connection.db.run('BEGIN TRANSACTION'); transactions(); - db.run('END TRANSACTION', error => { + connection.db.run('END TRANSACTION', error => { if (error) { - this.handleSQLiteError(error, `[storage ${this.name}] transaction(): ${error}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] transaction(): ${error}`); return reject(error); } @@ -653,11 +654,11 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private prepare(db: Database, sql: string, runCallback: (stmt: Statement) => void, errorDetails: () => string): void { - const stmt = db.prepare(sql); + private prepare(connection: IDatabaseConnection, sql: string, runCallback: (stmt: Statement) => void, errorDetails: () => string): void { + const stmt = connection.db.prepare(sql); const statementErrorListener = error => { - this.handleSQLiteError(error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); + this.handleSQLiteError(connection, error, `[storage ${this.name}] prepare(): ${error} (${sql}). Details: ${errorDetails()}`); }; stmt.on('error', statementErrorListener); From 49607e5746d75cb1faaa8ca6d973a46b92f6bcf3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 10 Jan 2019 09:16:04 +0100 Subject: [PATCH 0136/1066] strict null - Map.get(K) can return undefined --- src/typings/lib.ie11_safe_es6.d.ts | 2 +- src/vs/editor/contrib/snippet/snippetSession.ts | 10 +++++----- src/vs/workbench/browser/parts/views/views.ts | 8 ++++---- .../extensions/node/extensionsWorkbenchService.ts | 2 +- src/vs/workbench/services/search/node/fileSearch.ts | 8 ++++---- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/typings/lib.ie11_safe_es6.d.ts b/src/typings/lib.ie11_safe_es6.d.ts index e4f8fc57e76..ef2e2db7da5 100644 --- a/src/typings/lib.ie11_safe_es6.d.ts +++ b/src/typings/lib.ie11_safe_es6.d.ts @@ -10,7 +10,7 @@ interface Map { clear(): void; delete(key: K): boolean; forEach(callbackfn: (value: V, index: K, map: Map) => void, thisArg?: any): void; - get(key: K): V; + get(key: K): V | undefined; has(key: K): boolean; set(key: K, value?: V): Map; readonly size: number; diff --git a/src/vs/editor/contrib/snippet/snippetSession.ts b/src/vs/editor/contrib/snippet/snippetSession.ts index 8255cc35fd8..bf6c4909ac7 100644 --- a/src/vs/editor/contrib/snippet/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/snippetSession.ts @@ -108,7 +108,7 @@ export class OneSnippet { for (const placeholder of this._placeholderGroups[this._placeholderGroupsIdx]) { // Check if the placeholder has a transformation if (placeholder.transform) { - const id = this._placeholderDecorations.get(placeholder); + const id = this._placeholderDecorations.get(placeholder)!; const range = this._editor.getModel().getDecorationRange(id)!; const currentValue = this._editor.getModel().getValueInRange(range); @@ -158,7 +158,7 @@ export class OneSnippet { activePlaceholders.add(placeholder); for (const enclosingPlaceholder of this._snippet.enclosingPlaceholders(placeholder)) { - const id = this._placeholderDecorations.get(enclosingPlaceholder); + const id = this._placeholderDecorations.get(enclosingPlaceholder)!; accessor.changeDecorationOptions(id, enclosingPlaceholder.isFinalTabstop ? OneSnippet._decor.activeFinal : OneSnippet._decor.active); activePlaceholders.add(enclosingPlaceholder); } @@ -206,7 +206,7 @@ export class OneSnippet { result.set(placeholder.index, ranges); } - const id = this._placeholderDecorations.get(placeholder); + const id = this._placeholderDecorations.get(placeholder)!; const range = this._editor.getModel().getDecorationRange(id); if (!range) { // one of the placeholder lost its decoration and @@ -257,7 +257,7 @@ export class OneSnippet { // Remove the placeholder at which position are inserting // the snippet and also remove its decoration. - const id = this._placeholderDecorations.get(placeholder); + const id = this._placeholderDecorations.get(placeholder)!; accessor.removeDecoration(id); this._placeholderDecorations.delete(placeholder); @@ -574,7 +574,7 @@ export class SnippetSession { // add selections from 'this' snippet so that we know all // selections for this placeholder allPossibleSelections.forEach((array, index) => { - array.push(...possibleSelections.get(index)); + array.push(...possibleSelections.get(index)!); }); } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 8182e15a11b..44ff6623fd2 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -204,7 +204,7 @@ export class ContributableViewsModel extends Disposable { readonly viewDescriptors: IViewDescriptor[] = []; get visibleViewDescriptors(): IViewDescriptor[] { - return this.viewDescriptors.filter(v => this.viewStates.get(v.id).visible); + return this.viewDescriptors.filter(v => this.viewStates.get(v.id)!.visible); } private _onDidAdd = this._register(new Emitter()); @@ -298,7 +298,7 @@ export class ContributableViewsModel extends Disposable { move(this.viewDescriptors, fromIndex, toIndex); for (let index = 0; index < this.viewDescriptors.length; index++) { - const state = this.viewStates.get(this.viewDescriptors[index].id); + const state = this.viewStates.get(this.viewDescriptors[index].id)!; state.order = index; } @@ -388,7 +388,7 @@ export class ContributableViewsModel extends Disposable { } for (const viewDescriptor of splice.toInsert) { - const state = this.viewStates.get(viewDescriptor.id); + const state = this.viewStates.get(viewDescriptor.id)!; if (state.visible) { toAdd.push({ index: startIndex++, viewDescriptor, size: state.size, collapsed: state.collapsed }); @@ -536,7 +536,7 @@ export class ViewsService extends Disposable implements IViewsService { } getViewDescriptors(container: ViewContainer): IViewDescriptorCollection { - return this.viewDescriptorCollections.get(container); + return this.viewDescriptorCollections.get(container)!; } openView(id: string, focus: boolean): Promise { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 0c533a55045..ba095c6890a 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -341,7 +341,7 @@ class ExtensionDependencies implements IExtensionDependencies { if (!this.hasDependencies) { return []; } - return this._extension.dependencies.map(id => new ExtensionDependencies(this._map.get(id), id, this._map, this)); + return this._extension.dependencies.map(id => new ExtensionDependencies(this._map.get(id)!, id, this._map, this)); } private computeHasDependencies(): boolean { diff --git a/src/vs/workbench/services/search/node/fileSearch.ts b/src/vs/workbench/services/search/node/fileSearch.ts index 4554f18097d..138360c0ccc 100644 --- a/src/vs/workbench/services/search/node/fileSearch.ts +++ b/src/vs/workbench/services/search/node/fileSearch.ts @@ -203,7 +203,7 @@ export class FileWalker { 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); + 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; @@ -286,7 +286,7 @@ export class FileWalker { * Public for testing. */ spawnFindCmd(folderQuery: IFolderQuery) { - const excludePattern = this.folderExcludePatterns.get(folderQuery.folder.fsPath); + const excludePattern = this.folderExcludePatterns.get(folderQuery.folder.fsPath)!; const basenames = excludePattern.getBasenameTerms(); const pathTerms = excludePattern.getPathTerms(); const args = ['-L', '.']; @@ -428,7 +428,7 @@ export class FileWalker { private matchDirectoryTree({ rootEntries, pathToEntries }: IDirectoryTree, rootFolder: string, onResult: (result: IRawFileMatch) => void) { const self = this; - const excludePattern = this.folderExcludePatterns.get(rootFolder); + const excludePattern = this.folderExcludePatterns.get(rootFolder)!; const filePattern = this.filePattern; function matchDirectory(entries: IDirectoryEntry[]) { self.directoriesWalked++; @@ -508,7 +508,7 @@ export class FileWalker { // to ignore filtering by siblings because the user seems to know what she // is searching for and we want to include the result in that case anyway const currentRelativePath = relativeParentPath ? [relativeParentPath, file].join(path.sep) : file; - if (this.folderExcludePatterns.get(folderQuery.folder.fsPath).test(currentRelativePath, file, this.config.filePattern !== file ? hasSibling : undefined)) { + if (this.folderExcludePatterns.get(folderQuery.folder.fsPath)!.test(currentRelativePath, file, this.config.filePattern !== file ? hasSibling : undefined)) { return clb(null); } From 07b86facc7459836e5aae16d0080ac1a00a44761 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 10:08:01 +0100 Subject: [PATCH 0137/1066] list: make onDragStart optional --- src/vs/base/browser/ui/list/list.ts | 2 +- src/vs/base/browser/ui/list/listView.ts | 4 +++- src/vs/base/browser/ui/list/listWidget.ts | 10 ++++++++-- src/vs/base/browser/ui/tree/abstractTree.ts | 10 ++++++++-- src/vs/base/browser/ui/tree/asyncDataTree.ts | 16 +++++++++++----- .../electron-browser/watchExpressionsView.ts | 4 ---- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 074d10944de..55037c434ed 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -88,7 +88,7 @@ export const ListDragOverReactions = { export interface IListDragAndDrop { getDragURI(element: T): string | null; getDragLabel?(elements: T[]): string | undefined; - onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void; + onDragStart?(data: IDragAndDropData, originalEvent: DragEvent): void; onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 8a9bf0026c1..f8605ea43e7 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -632,7 +632,9 @@ export class ListView implements ISpliceable, IDisposable { this.currentDragData = new ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); - this.dnd.onDragStart(this.currentDragData, event); + if (this.dnd.onDragStart) { + this.dnd.onDragStart(this.currentDragData, event); + } } private onDragOver(event: IListDragEvent): boolean { diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index bb91459c2a2..30e2d62656a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -956,11 +956,17 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } getDragLabel?(elements: T[]): string | undefined { - return this.dnd.getDragLabel && this.dnd.getDragLabel(elements); + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(elements); + } + + return undefined; } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - this.dnd.onDragStart(data, originalEvent); + if (this.dnd.onDragStart) { + this.dnd.onDragStart(data, originalEvent); + } } onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b91ee43373d..7d6ea60ba3d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -28,11 +28,17 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } getDragLabel(nodes: ITreeNode[]): string | undefined { - return this.dnd.getDragLabel && this.dnd.getDragLabel(nodes.map(node => node.element)); + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element)); + } + + return undefined; } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - this.dnd.onDragStart(data, originalEvent); + if (this.dnd.onDragStart) { + this.dnd.onDragStart(data, originalEvent); + } } onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 91ea190331c..ada9d11c5eb 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -134,17 +134,23 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt getDragURI(node) { return options.dnd!.getDragURI(node.element as T); }, - getDragLabel: options.dnd!.getDragLabel && ((nodes) => { - return options.dnd!.getDragLabel!(nodes.map(node => node.element as T)); - }), + getDragLabel(nodes) { + if (options.dnd!.getDragLabel) { + return options.dnd!.getDragLabel!(nodes.map(node => node.element as T)); + } + + return undefined; + }, onDragStart(data, originalEvent) { - return options.dnd!.onDragStart(data, originalEvent); + if (options.dnd!.onDragStart) { + options.dnd!.onDragStart!(data, originalEvent); + } }, onDragOver(data, targetNode, targetIndex, originalEvent) { return options.dnd!.onDragOver(data, targetNode && targetNode.element as T, targetIndex, originalEvent); }, drop(data, targetNode, targetIndex, originalEvent) { - return options.dnd!.drop(data, targetNode && targetNode.element as T, targetIndex, originalEvent); + options.dnd!.drop(data, targetNode && targetNode.element as T, targetIndex, originalEvent); } }, multipleSelectionController: options.multipleSelectionController && { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index ba2c6babe7a..c0bbae43391 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -288,10 +288,6 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { return elements[0].name; } - onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - // noop - } - drop(data: IDragAndDropData, targetElement: IExpression, targetIndex: number, originalEvent: DragEvent): void { const draggedData = data.getData(); if (Array.isArray(draggedData)) { From 0214ede6247fbbd79a2b7567177e763559faffe2 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 10:08:25 +0100 Subject: [PATCH 0138/1066] :lipstick: --- .../parts/debug/electron-browser/watchExpressionsView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index c0bbae43391..bf0a1090d6d 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -263,7 +263,7 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { constructor(private debugService: IDebugService) { } - onDragOver(data: IDragAndDropData, targetElement: IExpression, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + onDragOver(data: IDragAndDropData): boolean | ITreeDragOverReaction { const draggedData = data.getData(); if (Array.isArray(draggedData) && draggedData[0] && draggedData[0].element.element instanceof Expression) { return true; @@ -280,7 +280,7 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { return element.getId(); } - getDragLabel?(elements: IExpression[]): string { + getDragLabel(elements: IExpression[]): string { if (elements.length > 1) { return String(elements.length); } @@ -288,7 +288,7 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { return elements[0].name; } - drop(data: IDragAndDropData, targetElement: IExpression, targetIndex: number, originalEvent: DragEvent): void { + drop(data: IDragAndDropData, targetElement: IExpression): void { const draggedData = data.getData(); if (Array.isArray(draggedData)) { const draggedElement = draggedData[0].element.element; From a4dd9113c99ee75cd24021269aa7af9c68c5c259 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 10:02:04 +0100 Subject: [PATCH 0139/1066] Use uris for changelog and readme of extension and transform them. --- src/vs/base/common/uriIpc.ts | 82 ++++++++++++++++++- .../sharedProcess/sharedProcessMain.ts | 3 +- .../common/extensionManagement.ts | 4 +- .../node/extensionManagementIpc.ts | 68 ++++++++------- .../node/extensionManagementService.ts | 4 +- .../node/extensionsWorkbenchService.ts | 30 +++---- .../services/extensions/node/rpcProtocol.ts | 73 +---------------- 7 files changed, 136 insertions(+), 128 deletions(-) diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 374ffd99055..10e24307da0 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; +import { MarshalledObject } from 'vs/base/common/marshalling'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; @@ -21,4 +22,83 @@ export const DefaultURITransformer: IURITransformer = new class { transformOutgoing(uri: URI | UriComponents): URI | UriComponents { return uri; } -}; \ No newline at end of file +}; + +function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + if (obj instanceof URI) { + return transformer.transformOutgoing(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformOutgoingURIs(obj[key], transformer, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformOutgoingURIs(obj, transformer, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + + +function _transformIncomingURIs(obj: any, transformer: IURITransformer, revive: boolean, depth: number): any { + + if (!obj || depth > 200) { + return null; + } + + if (typeof obj === 'object') { + + if ((obj).$mid === 1) { + return revive ? URI.revive(transformer.transformIncoming(obj)) : transformer.transformIncoming(obj); + } + + // walk object (or array) + for (let key in obj) { + if (Object.hasOwnProperty.call(obj, key)) { + const r = _transformIncomingURIs(obj[key], transformer, revive, depth + 1); + if (r !== null) { + obj[key] = r; + } + } + } + } + + return null; +} + +export function transformIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, false, 0); + if (result === null) { + // no change + return obj; + } + return result; +} + +export function transformAndReviveIncomingURIs(obj: T, transformer: IURITransformer): T { + const result = _transformIncomingURIs(obj, transformer, true, 0); + if (result === null) { + // no change + return obj; + } + return result; +} \ No newline at end of file diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index fb0305ca329..7c52379bc9b 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -43,7 +43,6 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/node/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { StaticRouter } from 'vs/base/parts/ipc/node/ipc'; -import { DefaultURITransformer } from 'vs/base/common/uriIpc'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -133,7 +132,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I instantiationService2.invokeFunction(accessor => { const extensionManagementService = accessor.get(IExtensionManagementService); - const channel = new ExtensionManagementChannel(extensionManagementService, () => DefaultURITransformer); + const channel = new ExtensionManagementChannel(extensionManagementService, () => null); server.registerChannel('extensions', channel); // clean up deprecated extensions diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index a6bdb826346..ff5a8cfff1b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -223,8 +223,8 @@ export interface ILocalExtension { manifest: IExtensionManifest; metadata: IGalleryMetadata; location: URI; - readmeUrl: string; - changelogUrl: string; + readmeUrl: URI | null; + changelogUrl: URI | null; } export const IExtensionManagementService = createDecorator('extensionManagementService'); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index a1f0d010abf..07fabbbe8f4 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -6,8 +6,30 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; import { Event } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; -import { IURITransformer } from 'vs/base/common/uriIpc'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; +import { cloneAndChange } from 'vs/base/common/objects'; + +function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { + return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); +} + +function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI { + return transformer ? transformer.transformOutgoing(uri) : uri; +} + +function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { + transformer = transformer ? transformer : DefaultURITransformer; + const manfiest = extension.manifest; + extension.manifest = undefined; + extension = transformAndReviveIncomingURIs(extension, transformer); + extension.manifest = manfiest; + return extension; +} + +function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { + return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoing(value) : undefined) : extension; +} export class ExtensionManagementChannel implements IServerChannel { @@ -16,7 +38,7 @@ export class ExtensionManagementChannel implements IServerChannel { onUninstallExtension: Event; onDidUninstallExtension: Event; - constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer) { + constructor(private service: IExtensionManagementService, private getUriTransformer: (requestContext: any) => IURITransformer | null) { this.onInstallExtension = Event.buffer(service.onInstallExtension, true); this.onDidInstallExtension = Event.buffer(service.onDidInstallExtension, true); this.onUninstallExtension = Event.buffer(service.onUninstallExtension, true); @@ -27,7 +49,7 @@ export class ExtensionManagementChannel implements IServerChannel { const uriTransformer = this.getUriTransformer(context); switch (event) { case 'onInstallExtension': return this.onInstallExtension; - case 'onDidInstallExtension': return Event.map(this.onDidInstallExtension, i => ({ ...i, local: this._transformOutgoing(i.local, uriTransformer) })); + case 'onDidInstallExtension': return Event.map(this.onDidInstallExtension, i => ({ ...i, local: transformOutgoingExtension(i.local, uriTransformer) })); case 'onUninstallExtension': return this.onUninstallExtension; case 'onDidUninstallExtension': return this.onDidUninstallExtension; } @@ -36,31 +58,21 @@ export class ExtensionManagementChannel implements IServerChannel { } call(context, command: string, args?: any): Promise { - const uriTransformer = this.getUriTransformer(context); + const uriTransformer: IURITransformer | null = this.getUriTransformer(context); switch (command) { - case 'zip': return this.service.zip(this._transformIncoming(args[0], uriTransformer)).then(uri => uriTransformer.transformOutgoing(uri)); - case 'unzip': return this.service.unzip(URI.revive(uriTransformer.transformIncoming(args[0])), args[1]); - case 'install': return this.service.install(URI.revive(uriTransformer.transformIncoming(args[0]))); + case 'zip': return this.service.zip(transformIncomingExtension(args[0], uriTransformer)).then(uri => transformOutgoingURI(uri, uriTransformer)); + case 'unzip': return this.service.unzip(transformIncomingURI(args[0], uriTransformer), args[1]); + case 'install': return this.service.install(transformIncomingURI(args[0], uriTransformer)); case 'installFromGallery': return this.service.installFromGallery(args[0]); - case 'uninstall': return this.service.uninstall(this._transformIncoming(args[0], uriTransformer), args[1]); - case 'reinstallFromGallery': return this.service.reinstallFromGallery(this._transformIncoming(args[0], uriTransformer)); - case 'getInstalled': return this.service.getInstalled(args[0]).then(extensions => extensions.map(e => this._transformOutgoing(e, uriTransformer))); - case 'updateMetadata': return this.service.updateMetadata(this._transformIncoming(args[0], uriTransformer), args[1]).then(e => this._transformOutgoing(e, uriTransformer)); + case 'uninstall': return this.service.uninstall(transformIncomingExtension(args[0], uriTransformer), args[1]); + case 'reinstallFromGallery': return this.service.reinstallFromGallery(transformIncomingExtension(args[0], uriTransformer)); + case 'getInstalled': return this.service.getInstalled(args[0]).then(extensions => extensions.map(e => transformOutgoingExtension(e, uriTransformer))); + case 'updateMetadata': return this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1]).then(e => transformOutgoingExtension(e, uriTransformer)); case 'getExtensionsReport': return this.service.getExtensionsReport(); } throw new Error('Invalid call'); } - - private _transformIncoming(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension { - return extension ? { ...extension, ...{ location: URI.revive(uriTransformer.transformIncoming(extension.location)) } } : extension; - } - - private _transformOutgoing(extension: ILocalExtension, uriTransformer: IURITransformer): ILocalExtension; - private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined; - private _transformOutgoing(extension: ILocalExtension | undefined, uriTransformer: IURITransformer): ILocalExtension | undefined { - return extension ? { ...extension, ...{ location: uriTransformer.transformOutgoing(extension.location) } } : extension; - } } export class ExtensionManagementChannelClient implements IExtensionManagementService { @@ -70,7 +82,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer constructor(private channel: IChannel) { } get onInstallExtension(): Event { return this.channel.listen('onInstallExtension'); } - get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: this._transformIncoming(i.local) })); } + get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: transformIncomingExtension(i.local, null) })); } get onUninstallExtension(): Event { return this.channel.listen('onUninstallExtension'); } get onDidUninstallExtension(): Event { return this.channel.listen('onDidUninstallExtension'); } @@ -100,21 +112,15 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer getInstalled(type: LocalExtensionType | null = null): Promise { return Promise.resolve(this.channel.call('getInstalled', [type])) - .then(extensions => extensions.map(extension => this._transformIncoming(extension))); + .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { return Promise.resolve(this.channel.call('updateMetadata', [local, metadata])) - .then(extension => this._transformIncoming(extension)); + .then(extension => transformIncomingExtension(extension, null)); } getExtensionsReport(): Promise { return Promise.resolve(this.channel.call('getExtensionsReport')); } - - private _transformIncoming(extension: ILocalExtension): ILocalExtension; - private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined; - private _transformIncoming(extension: ILocalExtension | undefined): ILocalExtension | undefined { - return extension ? { ...extension, ...{ location: URI.revive(extension.location) } } : extension; - } } \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index b2b55c6f524..69e32ce2152 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -776,9 +776,9 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(children => readManifest(extensionPath) .then(({ manifest, metadata }) => { const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; - const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)).toString() : null; + const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : null; const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; - const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)).toString() : null; + const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null; if (manifest.extensionDependencies) { manifest.extensionDependencies = manifest.extensionDependencies.map(id => adoptToGalleryExtensionId(id)); } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index ba095c6890a..fd01f0e7dd1 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { readFile } from 'vs/base/node/pfs'; import * as semver from 'semver'; import { Event, Emitter } from 'vs/base/common/event'; import { index, distinct } from 'vs/base/common/arrays'; @@ -37,6 +36,7 @@ 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'; interface IExtensionStateProvider { (extension: Extension): T; @@ -53,7 +53,8 @@ class Extension implements IExtension { public locals: ILocalExtension[], public gallery: IGalleryExtension | undefined, private telemetryService: ITelemetryService, - private logService: ILogService + private logService: ILogService, + private fileService: IFileService ) { } get type(): LocalExtensionType | undefined { @@ -237,8 +238,7 @@ class Extension implements IExtension { } if (this.local && this.local.readmeUrl) { - const uri = URI.parse(this.local.readmeUrl); - return readFile(uri.fsPath, 'utf8'); + return this.fileService.resolveContent(this.local.readmeUrl, { encoding: 'utf8' }).then(content => content.value); } if (this.type === LocalExtensionType.System) { @@ -258,8 +258,7 @@ ${this.description} } if (this.local && this.local.changelogUrl) { - const uri = URI.parse(this.local.changelogUrl); - return uri.scheme === 'file'; + return true; } return this.type === LocalExtensionType.System; @@ -280,13 +279,7 @@ ${this.description} return Promise.reject(new Error('not available')); } - const uri = URI.parse(changelogUrl); - - if (uri.scheme === 'file') { - return readFile(uri.fsPath, 'utf8'); - } - - return Promise.reject(new Error('not available')); + return this.fileService.resolveContent(changelogUrl, { encoding: 'utf8' }).then(content => content.value); } get dependencies(): string[] { @@ -391,7 +384,8 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, @IProgressService2 private readonly progressService: IProgressService2, @IExtensionService private readonly runtimeExtensionService: IExtensionService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, - @IStorageService private readonly storageService: IStorageService + @IStorageService private readonly storageService: IStorageService, + @IFileService private readonly fileService: IFileService ) { this.stateProvider = ext => this.getExtensionState(ext); @@ -443,7 +437,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, const locals = groupById[getGalleryExtensionIdFromLocal(local)]; 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); + 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; @@ -561,7 +555,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.syncLocalWithGalleryExtension(result, gallery); } } else { - result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService); + result = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService, this.fileService); } if (maliciousExtensionSet.has(result.identifier.id)) { @@ -920,7 +914,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); + extension = new Extension(this.galleryService, this.stateProvider, [], gallery, this.telemetryService, this.logService, this.fileService); } this.installing.push(extension); @@ -931,7 +925,7 @@ 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) : undefined; + let extension: Extension | undefined = installingExtension ? installingExtension : zipPath ? new Extension(this.galleryService, this.stateProvider, local ? [local] : [], undefined, this.telemetryService, this.logService, this.fileService) : undefined; if (extension) { this.installing = installingExtension ? this.installing.filter(e => e !== installingExtension) : this.installing; if (local) { diff --git a/src/vs/workbench/services/extensions/node/rpcProtocol.ts b/src/vs/workbench/services/extensions/node/rpcProtocol.ts index 33e6b81c70d..4d75df47757 100644 --- a/src/vs/workbench/services/extensions/node/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/node/rpcProtocol.ts @@ -9,9 +9,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { MarshalledObject } from 'vs/base/common/marshalling'; -import { URI } from 'vs/base/common/uri'; -import { IURITransformer } from 'vs/base/common/uriIpc'; +import { IURITransformer, transformIncomingURIs } from 'vs/base/common/uriIpc'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { LazyPromise } from 'vs/workbench/services/extensions/node/lazyPromise'; import { IRPCProtocol, ProxyIdentifier, getStringIdentifierForProxy } from 'vs/workbench/services/extensions/node/proxyIdentifier'; @@ -40,75 +38,6 @@ function createURIReplacer(transformer: IURITransformer | null): JSONStringifyRe }; } -function _transformOutgoingURIs(obj: any, transformer: IURITransformer, depth: number): any { - - if (!obj || depth > 200) { - return null; - } - - if (typeof obj === 'object') { - if (obj instanceof URI) { - return transformer.transformOutgoing(obj); - } - - // walk object (or array) - for (let key in obj) { - if (Object.hasOwnProperty.call(obj, key)) { - const r = _transformOutgoingURIs(obj[key], transformer, depth + 1); - if (r !== null) { - obj[key] = r; - } - } - } - } - - return null; -} - -export function transformOutgoingURIs(obj: T, transformer: IURITransformer): T { - const result = _transformOutgoingURIs(obj, transformer, 0); - if (result === null) { - // no change - return obj; - } - return result; -} - -function _transformIncomingURIs(obj: any, transformer: IURITransformer, depth: number): any { - - if (!obj || depth > 200) { - return null; - } - - if (typeof obj === 'object') { - - if ((obj).$mid === 1) { - return transformer.transformIncoming(obj); - } - - // walk object (or array) - for (let key in obj) { - if (Object.hasOwnProperty.call(obj, key)) { - const r = _transformIncomingURIs(obj[key], transformer, depth + 1); - if (r !== null) { - obj[key] = r; - } - } - } - } - - return null; -} - -function transformIncomingURIs(obj: T, transformer: IURITransformer): T { - const result = _transformIncomingURIs(obj, transformer, 0); - if (result === null) { - // no change - return obj; - } - return result; -} - export const enum RequestInitiator { LocalSide = 0, OtherSide = 1 From c256b075e1a1d041436058d81c8e93ed5f625142 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 10:06:09 +0100 Subject: [PATCH 0140/1066] null check --- .../extensionManagement/node/extensionManagementIpc.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index 07fabbbe8f4..a7636486c47 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -21,7 +21,7 @@ function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): UR function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { transformer = transformer ? transformer : DefaultURITransformer; const manfiest = extension.manifest; - extension.manifest = undefined; + delete extension.manifest; extension = transformAndReviveIncomingURIs(extension, transformer); extension.manifest = manfiest; return extension; @@ -49,7 +49,7 @@ export class ExtensionManagementChannel implements IServerChannel { const uriTransformer = this.getUriTransformer(context); switch (event) { case 'onInstallExtension': return this.onInstallExtension; - case 'onDidInstallExtension': return Event.map(this.onDidInstallExtension, i => ({ ...i, local: transformOutgoingExtension(i.local, uriTransformer) })); + case 'onDidInstallExtension': return Event.map(this.onDidInstallExtension, i => ({ ...i, local: i.local ? transformOutgoingExtension(i.local, uriTransformer) : i.local })); case 'onUninstallExtension': return this.onUninstallExtension; case 'onDidUninstallExtension': return this.onDidUninstallExtension; } @@ -82,7 +82,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer constructor(private channel: IChannel) { } get onInstallExtension(): Event { return this.channel.listen('onInstallExtension'); } - get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: transformIncomingExtension(i.local, null) })); } + get onDidInstallExtension(): Event { return Event.map(this.channel.listen('onDidInstallExtension'), i => ({ ...i, local: i.local ? transformIncomingExtension(i.local, null) : i.local })); } get onUninstallExtension(): Event { return this.channel.listen('onUninstallExtension'); } get onDidUninstallExtension(): Event { return this.channel.listen('onDidUninstallExtension'); } From 218a50826eb9561fbda886683abbeb3ab712bb30 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 10:10:20 +0100 Subject: [PATCH 0141/1066] more :lipstick: --- .../electron-browser/watchExpressionsView.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index bf0a1090d6d..2b8b1701f77 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -265,14 +265,11 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { onDragOver(data: IDragAndDropData): boolean | ITreeDragOverReaction { const draggedData = data.getData(); - if (Array.isArray(draggedData) && draggedData[0] && draggedData[0].element.element instanceof Expression) { - return true; - } - return false; + return Array.isArray(draggedData) && draggedData[0] && draggedData[0].element.element instanceof Expression; } - getDragURI(element: IExpression): string { + getDragURI(element: IExpression): string | null { if (!(element instanceof Expression) || element === this.debugService.getViewModel().getSelectedExpression()) { return null; } @@ -280,16 +277,17 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { return element.getId(); } - getDragLabel(elements: IExpression[]): string { - if (elements.length > 1) { - return String(elements.length); + getDragLabel(elements: IExpression[]): string | undefined { + if (elements.length === 1) { + return elements[0].name; } - return elements[0].name; + return undefined; } drop(data: IDragAndDropData, targetElement: IExpression): void { const draggedData = data.getData(); + if (Array.isArray(draggedData)) { const draggedElement = draggedData[0].element.element; const watches = this.debugService.getModel().getWatchExpressions(); From 4d285a7ee17f39b871dc3d3e16063d2233de21ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 10:49:33 +0100 Subject: [PATCH 0142/1066] Fix #66030 --- .../node/extensionLifecycle.ts | 2 +- src/vs/vscode.d.ts | 9 +++++++++ src/vs/vscode.proposed.d.ts | 15 --------------- .../workbench/api/node/extHostExtensionService.ts | 6 +++--- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 9238c8e500f..9b81e959837 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -130,6 +130,6 @@ export class ExtensionsLifecycle extends Disposable { } private getExtensionStoragePath(extension: ILocalExtension): string { - return posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLocaleLowerCase()); + return posix.join(this.environmentService.globalStorageHome, extension.galleryIdentifier.id.toLowerCase()); } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 367335194a6..f896c56d5af 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -4669,6 +4669,15 @@ declare module 'vscode' { */ storagePath: string | undefined; + /** + * An absolute file path in which the extension can store gloabal state. + * The directory might not exist on disk and creation is + * up to the extension. However, the parent directory is guaranteed to be existent. + * + * Use [`globalState`](#ExtensionContext.globalState) to store key value data. + */ + globalStoragePath: string; + /** * An absolute file path of a directory in which the extension can create log files. * The directory might not exist on disk and creation is up to the extension. However, diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 2b0a298d872..edbf4b240fa 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1088,21 +1088,6 @@ declare module 'vscode' { } //#endregion - //#region Extension Context - export interface ExtensionContext { - - /** - * An absolute file path in which the extension can store gloabal state. - * The directory might not exist on disk and creation is - * up to the extension. However, the parent directory is guaranteed to be existent. - * - * Use [`globalState`](#ExtensionContext.globalState) to store key value data. - */ - globalStoragePath: string; - - } - //#endregion - //#region SignatureHelpContext active paramters - mjbvz export interface SignatureHelpContext { /** diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index e574496358e..2708455fc7a 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -19,7 +19,7 @@ import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionA import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; import { ExtHostStorage } from 'vs/workbench/api/node/extHostStorage'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { IExtensionDescription, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +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 { CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -104,7 +104,7 @@ class ExtensionStoragePath { } globalValue(extension: IExtensionDescription): string { - return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value); + return path.join(this._environment.globalStorageHome.fsPath, extension.identifier.value.toLowerCase()); } private async _getOrCreateWorkspaceStoragePath(): Promise { @@ -422,7 +422,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { subscriptions: [], get extensionPath() { return extensionDescription.extensionLocation.fsPath; }, storagePath: this._storagePath.workspaceValue(extensionDescription), - get globalStoragePath(): string { checkProposedApiEnabled(extensionDescription); return that._storagePath.globalValue(extensionDescription); }, + globalStoragePath: this._storagePath.globalValue(extensionDescription), asAbsolutePath: (relativePath: string) => { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); }, logPath: that._extHostLogService.getLogDirectory(extensionDescription.identifier) }); From 4f603141de608bf6eba9e59c166dcf9c46ca8cdc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 11:07:44 +0100 Subject: [PATCH 0143/1066] Fix #66210 --- src/vs/workbench/browser/parts/statusbar/statusbarPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 93559ebac8d..d4c461f220d 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -285,7 +285,7 @@ class StatusBarEntryItem implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => el, - getActionsContext: () => this.entry.extensionId, + getActionsContext: () => this.entry.extensionId.value, getActions: () => [manageExtensionAction] }); })); From f860171e1961e5007e98359860d377dda729bb4a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 11:22:33 +0100 Subject: [PATCH 0144/1066] null check --- src/vs/workbench/browser/parts/statusbar/statusbarPart.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index d4c461f220d..29e6b41dfdc 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -285,7 +285,7 @@ class StatusBarEntryItem implements IStatusbarItem { this.contextMenuService.showContextMenu({ getAnchor: () => el, - getActionsContext: () => this.entry.extensionId.value, + getActionsContext: () => this.entry.extensionId!.value, getActions: () => [manageExtensionAction] }); })); From 4bbfaa2bde2665cffac1de5fecf04b4025b81b77 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 11:14:10 +0100 Subject: [PATCH 0145/1066] list: fix drop target style with hover related to #66291 --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 30e2d62656a..8dbfb0996f7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -687,7 +687,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listHoverBackground) { - content.push(`.monaco-list${suffix} .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`); + content.push(`.monaco-list${suffix}:not(.drop-target) .monaco-list-row:hover { background-color: ${styles.listHoverBackground}; }`); } if (styles.listHoverForeground) { From cb123bcc4df1dbf5adaddccb5200130924de3cb5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 11:25:35 +0100 Subject: [PATCH 0146/1066] list: unpack drag and drop data related to #66291 --- src/vs/base/browser/ui/list/listView.ts | 28 ++++++++----------- src/vs/base/browser/ui/tree/abstractTree.ts | 10 ++++++- src/vs/base/browser/ui/tree/asyncDataTree.ts | 11 +++++++- .../electron-browser/watchExpressionsView.ts | 8 ++++-- 4 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index f8605ea43e7..481fa107067 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -76,55 +76,51 @@ const DefaultOptions = { export class ElementsDragAndDropData implements IDragAndDropData { - private elements: T[]; + readonly elements: T[]; constructor(elements: T[]) { this.elements = elements; } - public update(dataTransfer: DataTransfer): void { - // no-op - } + update(): void { } - public getData(): any { + getData(): any { return this.elements; } } export class ExternalElementsDragAndDropData implements IDragAndDropData { - private elements: T[]; + readonly elements: T[]; constructor(elements: T[]) { this.elements = elements; } - public update(dataTransfer: DataTransfer): void { - // no-op - } + update(): void { } - public getData(): any { + getData(): any { return this.elements; } } export class DesktopDragAndDropData implements IDragAndDropData { - private types: any[]; - private files: any[]; + readonly types: any[]; + readonly files: any[]; constructor() { this.types = []; this.files = []; } - public update(dataTransfer: DataTransfer): void { + update(dataTransfer: DataTransfer): void { if (dataTransfer.types) { - this.types = [...dataTransfer.types]; + this.types.splice(0, this.types.length, ...dataTransfer.types); } if (dataTransfer.files) { - this.files = []; + this.files.splice(0, this.files.length); for (let i = 0; i < dataTransfer.files.length; i++) { const file = dataTransfer.files.item(i); @@ -136,7 +132,7 @@ export class DesktopDragAndDropData implements IDragAndDropData { } } - public getData(): any { + getData(): any { return { types: this.types, files: this.files diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 7d6ea60ba3d..a38cb342863 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -15,6 +15,7 @@ import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITre import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { range } from 'vs/base/common/arrays'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; class TreeNodeListDragAndDrop implements IListDragAndDrop> { @@ -42,7 +43,14 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { - const result = this.dnd.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); + let treeData: IDragAndDropData = data; + + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + treeData = new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + const result = this.dnd.onDragOver(treeData, targetNode && targetNode.element, targetIndex, originalEvent); const didChangeAutoExpandNode = this.autoExpandNode !== targetNode; if (didChangeAutoExpandNode) { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index ada9d11c5eb..bead97e19c2 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -13,6 +13,8 @@ import { timeout, always } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { toggleClass } from 'vs/base/browser/dom'; import { Iterator } from 'vs/base/common/iterator'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; enum AsyncDataTreeNodeState { Uninitialized, @@ -147,7 +149,14 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt } }, onDragOver(data, targetNode, targetIndex, originalEvent) { - return options.dnd!.onDragOver(data, targetNode && targetNode.element as T, targetIndex, originalEvent); + let treeData: IDragAndDropData = data; + + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + treeData = new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return options.dnd!.onDragOver(treeData, targetNode && targetNode.element as T, targetIndex, originalEvent); }, drop(data, targetNode, targetIndex, originalEvent) { options.dnd!.drop(data, targetNode && targetNode.element as T, targetIndex, originalEvent); diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 2b8b1701f77..581cfda36f7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -29,6 +29,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; @@ -264,9 +265,12 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { constructor(private debugService: IDebugService) { } onDragOver(data: IDragAndDropData): boolean | ITreeDragOverReaction { - const draggedData = data.getData(); + if (!(data instanceof ElementsDragAndDropData)) { + return false; + } - return Array.isArray(draggedData) && draggedData[0] && draggedData[0].element.element instanceof Expression; + const expressions = (data as ElementsDragAndDropData).elements; + return expressions.length > 0 && expressions[0] instanceof Expression; } getDragURI(element: IExpression): string | null { From b234f94af08673bd8272e86d67f5ec331e256512 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 11:43:55 +0100 Subject: [PATCH 0147/1066] fix #66212 --- src/vs/editor/contrib/gotoError/gotoErrorWidget.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/gotoErrorWidget.css index 2ecdf81af6f..7ca3f2a86e2 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.css +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.css @@ -6,7 +6,7 @@ /* marker zone */ .monaco-editor .marker-widget { - padding: 6px 12px; + padding: 3px 12px; text-overflow: ellipsis; white-space: nowrap; } From 052046e86cffb28f4d73aa0cc39c4ec769895812 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 11:44:08 +0100 Subject: [PATCH 0148/1066] :lipstick: --- src/vs/editor/contrib/gotoError/gotoErrorWidget.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index 9d1e6d0bed8..9b9c0bbe4b1 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -115,7 +115,8 @@ class MessageWidget { dom.clearNode(this._relatedBlock); if (isNonEmptyArray(relatedInformation)) { - this._relatedBlock.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * 0.66)}px`; + const relatedInformationNode = this._relatedBlock.appendChild(document.createElement('div')); + relatedInformationNode.style.paddingTop = `${Math.floor(this._editor.getConfiguration().lineHeight * 0.66)}px`; this._lines += 1; for (const related of relatedInformation) { @@ -136,7 +137,7 @@ class MessageWidget { container.appendChild(relatedMessage); this._lines += 1; - this._relatedBlock.appendChild(container); + relatedInformationNode.appendChild(container); } } From 6b71a612e95b090202e58c0e876bc46244814892 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Jan 2019 12:05:13 +0100 Subject: [PATCH 0149/1066] storage - implement recovery from cache (#66330) --- src/vs/base/node/storage.ts | 185 +++++++++++------- src/vs/base/test/node/storage/storage.test.ts | 48 ++++- 2 files changed, 148 insertions(+), 85 deletions(-) diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 99dc420236d..14936051232 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -6,12 +6,12 @@ import { Database, Statement } from 'vscode-sqlite3'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { ThrottledDelayer, timeout, always } from 'vs/base/common/async'; +import { ThrottledDelayer, timeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; import { mark } from 'vs/base/common/performance'; -import { rename, copy, renameIgnoreError } from 'vs/base/node/pfs'; +import { rename, copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; export enum StorageHint { @@ -42,7 +42,7 @@ export interface IStorageDatabase { getItems(): Promise>; updateItems(request: IUpdateRequest): Promise; - close(): Promise; + close(recovery?: () => Map): Promise; checkIntegrity(full: boolean): Promise; } @@ -273,7 +273,10 @@ export class Storage extends Disposable implements IStorage { // Trigger new flush to ensure data is persisted and then close // even if there is an error flushing. We must always ensure // the DB is closed to avoid corruption. - const onDone = () => this.database.close(); + // + // Recovery: we pass our cache over as recovery option in case + // the DB is not healthy. + const onDone = () => this.database.close(() => this.cache); return this.flushDelayer.trigger(() => this.flushPending(), 0 /* as soon as possible */).then(onDone, onDone); } @@ -303,8 +306,8 @@ interface IDatabaseConnection { isInMemory: boolean; - isCorrupted?: boolean; isErroneous?: boolean; + lastError?: string; } export interface ISQLiteStorageDatabaseOptions { @@ -359,6 +362,10 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } updateItems(request: IUpdateRequest): Promise { + return this.whenConnected.then(connection => this.doUpdateItems(connection, request)); + } + + private doUpdateItems(connection: IDatabaseConnection, request: IUpdateRequest): Promise { let updateCount = 0; if (request.insert) { updateCount += request.insert.size; @@ -375,90 +382,109 @@ export class SQLiteStorageDatabase implements IStorageDatabase { this.logger.trace(`[storage ${this.name}] updateItems(): insert(${request.insert ? mapToString(request.insert) : '0'}), delete(${request.delete ? setToString(request.delete) : '0'})`); } - return this.whenConnected.then(connection => { - return this.transaction(connection, () => { - if (request.insert && request.insert.size > 0) { - this.prepare(connection, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { - request.insert!.forEach((value, key) => { - stmt.run([key, value]); - }); - }, () => { - const keys: string[] = []; - let length = 0; - request.insert!.forEach((value, key) => { - keys.push(key); - length += value.length; - }); - - return `Keys: ${keys.join(', ')} Length: ${length}`; + return this.transaction(connection, () => { + if (request.insert && request.insert.size > 0) { + this.prepare(connection, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { + request.insert!.forEach((value, key) => { + stmt.run([key, value]); }); - } - - if (request.delete && request.delete.size) { - this.prepare(connection, 'DELETE FROM ItemTable WHERE key=?', stmt => { - request.delete!.forEach(key => { - stmt.run(key); - }); - }, () => { - const keys: string[] = []; - request.delete!.forEach(key => { - keys.push(key); - }); - - return `Keys: ${keys.join(', ')}`; + }, () => { + const keys: string[] = []; + let length = 0; + request.insert!.forEach((value, key) => { + keys.push(key); + length += value.length; }); - } - }); + + return `Keys: ${keys.join(', ')} Length: ${length}`; + }); + } + + if (request.delete && request.delete.size) { + this.prepare(connection, 'DELETE FROM ItemTable WHERE key=?', stmt => { + request.delete!.forEach(key => { + stmt.run(key); + }); + }, () => { + const keys: string[] = []; + request.delete!.forEach(key => { + keys.push(key); + }); + + return `Keys: ${keys.join(', ')}`; + }); + } }); } - close(): Promise { + close(recovery?: () => Map): Promise { this.logger.trace(`[storage ${this.name}] close()`); - return this.whenConnected.then(connection => { - return new Promise((resolve, reject) => { - connection.db.close(closeError => { - if (closeError) { - this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); - } + return this.whenConnected.then(connection => this.doClose(connection, recovery)); + } - if (connection.isInMemory) { - return resolve(); // return early for in-memory DBs - } + private doClose(connection: IDatabaseConnection, recovery?: () => Map): Promise { + return new Promise((resolve, reject) => { + connection.db.close(closeError => { + if (closeError) { + this.handleSQLiteError(connection, closeError, `[storage ${this.name}] close(): ${closeError}`); + } - // If the DB is corrupt, make sure to rename the file so that we can start - // from a fresh DB or a previous backup on the next startup and not be stuck - // with a corrupt DB for ever. - if (connection.isCorrupted) { - this.logger.error(`[storage ${this.name}] close(): removing corrupt DB and trying to restore backup`); + // Return early if this storage was created only in-memory + // e.g. when running tests we do not need to backup. + if (this.path === SQLiteStorageDatabase.IN_MEMORY_PATH) { + return resolve(); + } - return always(rename(this.path, this.toCorruptPath(this.path)) - .then(() => rename(this.toBackupPath(this.path), this.path)), () => closeError ? reject(closeError) : resolve()); - } - - if (closeError) { - return reject(closeError); - } - - // If the DB closed successfully and we are not running in-memory - // and the DB did not get corrupted during runtime, make a backup - // of the DB so that we can use it as fallback in case the actual - // DB becomes corrupt. - return this.backup(connection).then(resolve, error => { + // If the DB closed successfully and we are not running in-memory + // and the DB did not get errors during runtime, make a backup + // of the DB so that we can use it as fallback in case the actual + // DB becomes corrupt in the future. + if (!connection.isErroneous && !connection.isInMemory) { + return this.backup().then(resolve, error => { this.logger.error(`[storage ${this.name}] backup(): ${error}`); return resolve(); // ignore failing backup }); - }); + } + + // Recovery: if we detected errors while using the DB or we are using + // an inmemory DB (as a fallback to not being able to open the DB initially) + // and we have a recovery function provided, we recreate the DB with this + // data to recover all known data without loss if possible. + if (typeof recovery === 'function') { + + // Delete the existing DB. If the path does not exist or fails to + // be deleted, we do not try to recover anymore because we assume + // that the path is no longer writeable for us. + return unlink(this.path).then(() => { + + // Re-open the DB fresh + return this.doConnect(this.path).then(recoveryConnection => { + const closeRecoveryConnection = () => { + return this.doClose(recoveryConnection, undefined /* do not attempt to recover again */); + }; + + // Store items + return this.doUpdateItems(recoveryConnection, { insert: recovery() }).then(() => closeRecoveryConnection(), error => { + + // In case of an error updating items, still ensure to close the connection + // to prevent SQLITE_BUSY errors when the connection is restablished + closeRecoveryConnection(); + + return Promise.reject(error); + }); + }); + }).then(resolve, reject); + } + + // Finally without recovery we just reject + return reject(closeError || new Error('Database has errors or is in-memory without recovery option')); }); }); } - private backup(db: IDatabaseConnection): Promise { - if (db.isInMemory) { - return Promise.resolve(); // no backups when running in-memory - } - + private backup(): Promise { const backupPath = this.toBackupPath(this.path); return copy(this.path, backupPath); @@ -473,7 +499,17 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return this.whenConnected.then(connection => { return this.get(connection, full ? 'PRAGMA integrity_check' : 'PRAGMA quick_check').then(row => { - return full ? row['integrity_check'] : row['quick_check']; + const integrity = full ? row['integrity_check'] : row['quick_check']; + + if (connection.isErroneous) { + return `${integrity} (last error: ${connection.lastError})`; + } + + if (connection.isInMemory) { + return `${integrity} (in-memory!)`; + } + + return integrity; }); }); } @@ -525,10 +561,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { private handleSQLiteError(connection: IDatabaseConnection, error: Error & { code?: string }, msg: string): void { connection.isErroneous = true; - - if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - connection.isCorrupted = true; - } + connection.lastError = msg; this.logger.error(msg); } diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 603d22ea0e8..11384986e9b 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -8,7 +8,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'path'; import { tmpdir } from 'os'; import { equal, ok } from 'assert'; -import { mkdirp, del, writeFile, exists } from 'vs/base/node/pfs'; +import { mkdirp, del, writeFile, exists, unlink } from 'vs/base/node/pfs'; import { timeout } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import { isWindows } from 'vs/base/common/platform'; @@ -325,7 +325,14 @@ suite('SQLite Storage Library', () => { storedItems = await storage.getItems(); equal(storedItems.size, 0); - await storage.close(); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); } test('basics', async () => { @@ -333,7 +340,7 @@ suite('SQLite Storage Library', () => { await mkdirp(storageDir); - testDBBasics(join(storageDir, 'storage.db')); + await testDBBasics(join(storageDir, 'storage.db')); await del(storageDir, tmpdir()); }); @@ -393,7 +400,14 @@ suite('SQLite Storage Library', () => { equal(storedItems.get('some/foo/path'), 'some/bar/path'); equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); - await storage.close(); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); await del(storageDir, tmpdir()); }); @@ -427,7 +441,7 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); - test('basics (DB that becomes corrupt during runtime restores backup on close())', async () => { + test('basics (DB that becomes corrupt during runtime stores all state from cache on close)', async () => { if (isWindows) { await Promise.resolve(); // Windows will fail to write to open DB due to locking @@ -449,7 +463,8 @@ suite('SQLite Storage Library', () => { await storage.updateItems({ insert: items }); await storage.close(); - equal(await exists(`${storagePath}.backup`), true); + const backupPath = `${storagePath}.backup`; + equal(await exists(backupPath), true); storage = new SQLiteStorageDatabase(storagePath); await storage.getItems(); @@ -462,9 +477,17 @@ suite('SQLite Storage Library', () => { // on shutdown. await storage.checkIntegrity(true).then(null, error => { } /* error is expected here but we do not want to fail */); - await storage.close(); + await unlink(backupPath); // also test that the recovery DB is backed up properly - equal(await exists(`${storagePath}.backup`), false); + let recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return items; + }); + + equal(recoveryCalled, true); + equal(await exists(backupPath), true); storage = new SQLiteStorageDatabase(storagePath); @@ -474,7 +497,14 @@ suite('SQLite Storage Library', () => { equal(storedItems.get('some/foo/path'), 'some/bar/path'); equal(storedItems.get(JSON.stringify({ foo: 'bar' })), JSON.stringify({ bar: 'foo' })); - await storage.close(); + recoveryCalled = false; + await storage.close(() => { + recoveryCalled = true; + + return new Map(); + }); + + equal(recoveryCalled, false); await del(storageDir, tmpdir()); }); From d1a0be787b55660cbe2d4282acf936c3e5205f04 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 12:16:48 +0100 Subject: [PATCH 0150/1066] Fix #66260 --- .../parts/extensions/electron-browser/extensionTipsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 25a0dda9e30..ff2e944baae 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -372,7 +372,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (filteredWanted.length) { try { - let validRecommendations = (await this._galleryService.query({ names: filteredWanted })).firstPage + let validRecommendations = (await this._galleryService.query({ names: filteredWanted, pageSize: filteredWanted.length })).firstPage .map(extension => extension.identifier.id.toLowerCase()); if (validRecommendations.length !== filteredWanted.length) { From 2e551fc2b19684e8193ab1d691266e3ac75a49ec Mon Sep 17 00:00:00 2001 From: Kumar Harsh Date: Thu, 10 Jan 2019 17:14:42 +0530 Subject: [PATCH 0151/1066] fix(loc): fix incorrect phrasing for transparent color descriptions The sentence 'The color must not be opaque to not hide underlying decorations' is incorrect. This PR replaces 'to not hide' with 'so as not to hide'. See the link below for why: https://english.stackexchange.com/questions/45265/use-of-so-as-not-to Closes #66333 --- .../editor/common/view/editorColorRegistry.ts | 4 +-- .../wordHighlighter/wordHighlighter.ts | 8 ++--- src/vs/platform/theme/common/colorRegistry.ts | 32 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 6d3d197dc61..27ccaf58e6b 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -13,7 +13,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic */ export const editorLineHighlight = registerColor('editor.lineHighlightBackground', { dark: null, light: null, hc: null }, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hc: '#f38518' }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); -export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque to not hide underlying decorations.'), true); +export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hc: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.'), true); export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hc: Color.white }, nls.localize('caret', 'Color of the editor cursor.')); @@ -53,7 +53,7 @@ export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode. export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); -export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: new Color(new RGBA(18, 136, 18, 0.7)), light: new Color(new RGBA(18, 136, 18, 0.7)), hc: new Color(new RGBA(50, 255, 50, 1)) }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: new Color(new RGBA(18, 18, 136, 0.7)), light: new Color(new RGBA(18, 18, 136, 0.7)), hc: new Color(new RGBA(50, 50, 255, 1)) }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 9a7accdafbc..036f26483fb 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -27,13 +27,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { activeContrastBorder, editorSelectionHighlight, editorSelectionHighlightBorder, overviewRulerSelectionHighlightForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; -export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque to not hide underlying decorations.'), true); -export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque to not hide underlying decorations.'), true); +export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations.'), true); +export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWordHighlightBorder = registerColor('editor.wordHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('wordHighlightBorder', 'Border color of a symbol during read-access, like reading a variable.')); export const editorWordHighlightStrongBorder = registerColor('editor.wordHighlightStrongBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('wordHighlightStrongBorder', 'Border color of a symbol during write-access, like writing to a variable.')); -export const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque to not hide underlying decorations.'), true); -export const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0CC', light: '#C0A0C0CC', hc: '#C0A0C0CC' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +export const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0CC', light: '#C0A0C0CC', hc: '#C0A0C0CC' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 8d47f59a101..d239891f730 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -258,8 +258,8 @@ export const editorWidgetResizeBorder = registerColor('editorWidget.resizeBorder */ export const editorSelectionBackground = registerColor('editor.selectionBackground', { light: '#ADD6FF', dark: '#264F78', hc: '#f3f518' }, nls.localize('editorSelectionBackground', "Color of the editor selection.")); export const editorSelectionForeground = registerColor('editor.selectionForeground', { light: null, dark: null, hc: '#000000' }, nls.localize('editorSelectionForeground', "Color of the selected text for high contrast.")); -export const editorInactiveSelection = registerColor('editor.inactiveSelectionBackground', { light: transparent(editorSelectionBackground, 0.5), dark: transparent(editorSelectionBackground, 0.5), hc: transparent(editorSelectionBackground, 0.5) }, nls.localize('editorInactiveSelection', "Color of the selection in an inactive editor. The color must not be opaque to not hide underlying decorations."), true); -export const editorSelectionHighlight = registerColor('editor.selectionHighlightBackground', { light: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), dark: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), hc: null }, nls.localize('editorSelectionHighlight', 'Color for regions with the same content as the selection. The color must not be opaque to not hide underlying decorations.'), true); +export const editorInactiveSelection = registerColor('editor.inactiveSelectionBackground', { light: transparent(editorSelectionBackground, 0.5), dark: transparent(editorSelectionBackground, 0.5), hc: transparent(editorSelectionBackground, 0.5) }, nls.localize('editorInactiveSelection', "Color of the selection in an inactive editor. The color must not be opaque so as not to hide underlying decorations."), true); +export const editorSelectionHighlight = registerColor('editor.selectionHighlightBackground', { light: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), dark: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), hc: null }, nls.localize('editorSelectionHighlight', 'Color for regions with the same content as the selection. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorSelectionHighlightBorder = registerColor('editor.selectionHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('editorSelectionHighlightBorder', "Border color for regions with the same content as the selection.")); @@ -267,16 +267,16 @@ export const editorSelectionHighlightBorder = registerColor('editor.selectionHig * Editor find match colors. */ export const editorFindMatch = registerColor('editor.findMatchBackground', { light: '#A8AC94', dark: '#515C6A', hc: null }, nls.localize('editorFindMatch', "Color of the current search match.")); -export const editorFindMatchHighlight = registerColor('editor.findMatchHighlightBackground', { light: '#EA5C0055', dark: '#EA5C0055', hc: null }, nls.localize('findMatchHighlight', "Color of the other search matches. The color must not be opaque to not hide underlying decorations."), true); -export const editorFindRangeHighlight = registerColor('editor.findRangeHighlightBackground', { dark: '#3a3d4166', light: '#b4b4b44d', hc: null }, nls.localize('findRangeHighlight', "Color of the range limiting the search. The color must not be opaque to not hide underlying decorations."), true); +export const editorFindMatchHighlight = registerColor('editor.findMatchHighlightBackground', { light: '#EA5C0055', dark: '#EA5C0055', hc: null }, nls.localize('findMatchHighlight', "Color of the other search matches. The color must not be opaque so as not to hide underlying decorations."), true); +export const editorFindRangeHighlight = registerColor('editor.findRangeHighlightBackground', { dark: '#3a3d4166', light: '#b4b4b44d', hc: null }, nls.localize('findRangeHighlight', "Color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); export const editorFindMatchBorder = registerColor('editor.findMatchBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('editorFindMatchBorder', "Border color of the current search match.")); export const editorFindMatchHighlightBorder = registerColor('editor.findMatchHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('findMatchHighlightBorder', "Border color of the other search matches.")); -export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque to not hide underlying decorations."), true); +export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); /** * Editor hover */ -export const editorHoverHighlight = registerColor('editor.hoverHighlightBackground', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown. The color must not be opaque to not hide underlying decorations.'), true); +export const editorHoverHighlight = registerColor('editor.hoverHighlightBackground', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorHoverBackground = registerColor('editorHoverWidget.background', { light: editorWidgetBackground, dark: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('hoverBackground', 'Background color of the editor hover.')); export const editorHoverBorder = registerColor('editorHoverWidget.border', { light: editorWidgetBorder, dark: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('hoverBorder', 'Border color of the editor hover.')); @@ -291,8 +291,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 0.2)); export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 0.2)); -export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque to not hide underlying decorations.'), true); -export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque to not hide underlying decorations.'), true); +export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.'), true); +export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.'), true); export const diffInsertedOutline = registerColor('diffEditor.insertedTextBorder', { dark: null, light: null, hc: '#33ff2eff' }, nls.localize('diffEditorInsertedOutline', 'Outline color for the text that got inserted.')); export const diffRemovedOutline = registerColor('diffEditor.removedTextBorder', { dark: null, light: null, hc: '#FF008F' }, nls.localize('diffEditorRemovedOutline', 'Outline color for text that got removed.')); @@ -327,12 +327,12 @@ const commonBaseColor = Color.fromHex('#606060').transparent(0.4); const contentTransparency = 0.4; const rulerTransparency = 1; -export const mergeCurrentHeaderBackground = registerColor('merge.currentHeaderBackground', { dark: currentBaseColor, light: currentBaseColor, hc: null }, nls.localize('mergeCurrentHeaderBackground', 'Current header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCurrentContentBackground = registerColor('merge.currentContentBackground', { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hc: transparent(mergeCurrentHeaderBackground, contentTransparency) }, nls.localize('mergeCurrentContentBackground', 'Current content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeaderBackground', { dark: incomingBaseColor, light: incomingBaseColor, hc: null }, nls.localize('mergeIncomingHeaderBackground', 'Incoming header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeIncomingContentBackground = registerColor('merge.incomingContentBackground', { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hc: transparent(mergeIncomingHeaderBackground, contentTransparency) }, nls.localize('mergeIncomingContentBackground', 'Incoming content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBackground', { dark: commonBaseColor, light: commonBaseColor, hc: null }, nls.localize('mergeCommonHeaderBackground', 'Common ancestor header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCommonContentBackground = registerColor('merge.commonContentBackground', { dark: transparent(mergeCommonHeaderBackground, contentTransparency), light: transparent(mergeCommonHeaderBackground, contentTransparency), hc: transparent(mergeCommonHeaderBackground, contentTransparency) }, nls.localize('mergeCommonContentBackground', 'Common ancestor content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); +export const mergeCurrentHeaderBackground = registerColor('merge.currentHeaderBackground', { dark: currentBaseColor, light: currentBaseColor, hc: null }, nls.localize('mergeCurrentHeaderBackground', 'Current header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCurrentContentBackground = registerColor('merge.currentContentBackground', { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hc: transparent(mergeCurrentHeaderBackground, contentTransparency) }, nls.localize('mergeCurrentContentBackground', 'Current content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeaderBackground', { dark: incomingBaseColor, light: incomingBaseColor, hc: null }, nls.localize('mergeIncomingHeaderBackground', 'Incoming header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeIncomingContentBackground = registerColor('merge.incomingContentBackground', { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hc: transparent(mergeIncomingHeaderBackground, contentTransparency) }, nls.localize('mergeIncomingContentBackground', 'Incoming content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBackground', { dark: commonBaseColor, light: commonBaseColor, hc: null }, nls.localize('mergeCommonHeaderBackground', 'Common ancestor header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCommonContentBackground = registerColor('merge.commonContentBackground', { dark: transparent(mergeCommonHeaderBackground, contentTransparency), light: transparent(mergeCommonHeaderBackground, contentTransparency), hc: transparent(mergeCommonHeaderBackground, contentTransparency) }, nls.localize('mergeCommonContentBackground', 'Common ancestor content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeBorder = registerColor('merge.border', { dark: null, light: null, hc: '#C3DF6F' }, nls.localize('mergeBorder', 'Border color on headers and the splitter in inline merge-conflicts.')); @@ -341,9 +341,9 @@ export const overviewRulerIncomingContentForeground = registerColor('editorOverv export const overviewRulerCommonContentForeground = registerColor('editorOverviewRuler.commonContentForeground', { dark: transparent(mergeCommonHeaderBackground, rulerTransparency), light: transparent(mergeCommonHeaderBackground, rulerTransparency), hc: mergeBorder }, nls.localize('overviewRulerCommonContentForeground', 'Common ancestor overview ruler foreground for inline merge-conflicts.')); const findMatchColorDefault = new Color(new RGBA(246, 185, 77, 0.7)); -export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', { dark: findMatchColorDefault, light: findMatchColorDefault, hc: findMatchColorDefault }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', { dark: findMatchColorDefault, light: findMatchColorDefault, hc: findMatchColorDefault }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.'), true); -export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.'), true); // ----- color functions From 483fce634ff2e136438f227add693ec6d4763591 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 15:41:59 +0100 Subject: [PATCH 0152/1066] explorer drag and drop --- .../files/electron-browser/fileActions.ts | 110 +-- .../electron-browser/views/explorerView.ts | 5 +- .../electron-browser/views/explorerViewer.ts | 778 ++++++++++-------- 3 files changed, 438 insertions(+), 455 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 097b9ba355b..db6b2c76bcf 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -7,7 +7,7 @@ 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 { sequence, ITask, always } from 'vs/base/common/async'; +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'; @@ -16,8 +16,8 @@ import * as strings from 'vs/base/common/strings'; import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { VIEWLET_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; -import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; -import { IFileService, IFileStat, AutoSaveConfiguration } from 'vs/platform/files/common/files'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { IFileService, AutoSaveConfiguration } from 'vs/platform/files/common/files'; import { toResource, IUntitledResourceInput } from 'vs/workbench/common/editor'; import { ExplorerViewlet } from 'vs/workbench/parts/files/electron-browser/explorerViewlet'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; @@ -36,7 +36,7 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { IListService, ListWidget } from 'vs/platform/list/browser/listService'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Schemas } from 'vs/base/common/network'; -import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IConfirmationResult, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Constants } from 'vs/editor/common/core/uint'; @@ -431,106 +431,6 @@ class BaseDeleteFileAction extends BaseErrorReportingAction { } } -/* Add File */ -export class AddFilesAction extends BaseErrorReportingAction { - - constructor( - private element: ExplorerItem, - clazz: string, - @IFileService private readonly fileService: IFileService, - @IEditorService private readonly editorService: IEditorService, - @IDialogService private readonly dialogService: IDialogService, - @INotificationService notificationService: INotificationService, - @ITextFileService private readonly textFileService: ITextFileService, - @IExplorerService private readonly explorerService: IExplorerService - ) { - super('workbench.files.action.addFile', nls.localize('addFiles', "Add Files"), notificationService); - - if (clazz) { - this.class = clazz; - } - } - - public run(resourcesToAdd: URI[]): Promise { - if (resourcesToAdd && resourcesToAdd.length > 0) { - - // Find parent to add to - let targetElement: ExplorerItem; - if (this.element) { - targetElement = this.element; - } else { - targetElement = this.explorerService.roots[0]; - } - - if (!targetElement.isDirectory) { - targetElement = targetElement.parent; - } - - // Resolve target to check for name collisions and ask user - return this.fileService.resolveFile(targetElement.resource).then((targetStat: IFileStat) => { - - // Check for name collisions - const targetNames = new Set(); - targetStat.children.forEach((child) => { - targetNames.add(isLinux ? child.name : child.name.toLowerCase()); - }); - - let overwritePromise: Promise = Promise.resolve({ confirmed: true }); - if (resourcesToAdd.some(resource => { - return targetNames.has(!resources.hasToIgnoreCase(resource) ? resources.basename(resource) : resources.basename(resource).toLowerCase()); - })) { - const confirm: IConfirmation = { - message: nls.localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), - detail: nls.localize('irreversible', "This action is irreversible!"), - primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' - }; - - overwritePromise = this.dialogService.confirm(confirm); - } - - return overwritePromise.then(res => { - if (!res.confirmed) { - return undefined; - } - - // Run add in sequence - const addPromisesFactory: ITask>[] = []; - resourcesToAdd.forEach(resource => { - addPromisesFactory.push(() => { - const sourceFile = resource; - const targetFile = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); - - // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents - // of the target file would replace the contents of the added file. since we already - // confirmed the overwrite before, this is OK. - let revertPromise: Promise = Promise.resolve(null); - if (this.textFileService.isDirty(targetFile)) { - revertPromise = this.textFileService.revertAll([targetFile], { soft: true }); - } - - return revertPromise.then(() => { - const target = resources.joinPath(targetElement.resource, resources.basename(sourceFile)); - return this.fileService.copyFile(sourceFile, target, true).then(stat => { - - // if we only add one file, just open it directly - if (resourcesToAdd.length === 1) { - this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - } - }, error => this.onError(error)); - }); - }); - }); - - return sequence(addPromisesFactory); - }); - }); - } - - return Promise.resolve(undefined); - } -} - // Copy File/Folder class CopyFileAction extends BaseErrorReportingAction { @@ -603,7 +503,7 @@ class PasteFileAction extends BaseErrorReportingAction { } } -function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean }): URI { +export function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean }): URI { let name = resources.basenameOrAuthority(fileToPaste.resource); let candidate = resources.joinPath(targetFolder.resource, name); 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 72b5d85b424..e32a1a11cd2 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -31,7 +31,7 @@ import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IViewletPanelOptions, ViewletPanel } from 'vs/workbench/browser/parts/views/panelViewlet'; import { ILabelService } from 'vs/platform/label/common/label'; -import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter, FileSorter } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; +import { ExplorerDelegate, ExplorerAccessibilityProvider, ExplorerDataSource, FilesRenderer, FilesFilter, FileSorter, FileDragAndDrop } from 'vs/workbench/parts/files/electron-browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; @@ -237,7 +237,8 @@ export class ExplorerView extends ViewletPanel { }, multipleSelectionSupport: true, filter: this.filter, - sorter: this.instantiationService.createInstance(FileSorter) + sorter: this.instantiationService.createInstance(FileSorter), + dnd: this.instantiationService.createInstance(FileDragAndDrop) }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.disposables.push(this.tree); 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 77527fedd37..fec0dea1640 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -6,21 +6,21 @@ import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import * as DOM from 'vs/base/browser/dom'; import * as glob from 'vs/base/common/glob'; -import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IFileService, FileKind } from 'vs/platform/files/common/files'; +import { IFileService, FileKind, IFileStat, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IFileLabelOptions, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; -import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IFilesConfiguration, IExplorerService, IEditableData } from 'vs/workbench/parts/files/common/files'; -import { dirname, joinPath } from 'vs/base/common/resources'; +import { dirname, joinPath, isEqualOrParent, basename, hasToIgnoreCase, distinctParents } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; @@ -31,6 +31,21 @@ import { equals, deepClone } from 'vs/base/common/objects'; import * as path from 'path'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { compareFileExtensions, compareFileNames } from 'vs/base/common/comparers'; +import { fillResourceDataTransfers, CodeDataTransfers, extractResources } from 'vs/workbench/browser/dnd'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; +import { Schemas } from 'vs/base/common/network'; +import { DesktopDragAndDropData, ExternalElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { isMacintosh, isLinux } from 'vs/base/common/platform'; +import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; +import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { URI } from 'vs/base/common/uri'; +import { ITask, sequence } from 'vs/base/common/async'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { findValidPasteFileTarget } from 'vs/workbench/parts/files/electron-browser/fileActions'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -379,345 +394,412 @@ export class FileSorter implements ITreeSorter { } } -// export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { - -// private static readonly CONFIRM_DND_SETTING_KEY = 'explorer.confirmDragAndDrop'; - -// private toDispose: IDisposable[]; -// private dropEnabled: boolean; - -// constructor( -// @INotificationService private notificationService: INotificationService, -// @IDialogService private dialogService: IDialogService, -// @IWorkspaceContextService private contextService: IWorkspaceContextService, -// @IFileService private fileService: IFileService, -// @IConfigurationService private configurationService: IConfigurationService, -// @IInstantiationService instantiationService: IInstantiationService, -// @ITextFileService private textFileService: ITextFileService, -// @IWindowService private windowService: IWindowService, -// @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService -// ) { -// super(stat => this.statToResource(stat), instantiationService); - -// this.toDispose = []; - -// this.updateDropEnablement(); - -// this.registerListeners(); -// } - -// private statToResource(stat: ExplorerItem): URI { -// if (stat.isDirectory) { -// return URI.from({ scheme: 'folder', path: stat.resource.path }); // indicates that we are dragging a folder -// } - -// return stat.resource; -// } - -// private registerListeners(): void { -// this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => this.updateDropEnablement())); -// } - -// private updateDropEnablement(): void { -// this.dropEnabled = this.configurationService.getValue('explorer.enableDragAndDrop'); -// } - -// public onDragStart(tree: ITree, data: IDragAndDropData, originalEvent: DragMouseEvent): void { -// const sources: ExplorerItem[] = data.getData(); -// if (sources && sources.length) { - -// // When dragging folders, make sure to collapse them to free up some space -// sources.forEach(s => { -// if (s.isDirectory && tree.isExpanded(s)) { -// tree.collapse(s, false); -// } -// }); - -// // Apply some datatransfer types to allow for dragging the element outside of the application -// this.instantiationService.invokeFunction(fillResourceDataTransfers, sources, originalEvent); - -// // The only custom data transfer we set from the explorer is a file transfer -// // to be able to DND between multiple code file explorers across windows -// const fileResources = sources.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); -// if (fileResources.length) { -// originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); -// } -// } -// } - -// public onDragOver(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): IDragOverReaction { -// if (!this.dropEnabled) { -// return DRAG_OVER_REJECT; -// } - -// const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); -// const fromDesktop = data instanceof DesktopDragAndDropData; - -// // Desktop DND -// if (fromDesktop) { -// const types: string[] = originalEvent.dataTransfer.types; -// const typesArray: string[] = []; -// for (let i = 0; i < types.length; i++) { -// typesArray.push(types[i].toLowerCase()); // somehow the types are lowercase -// } - -// if (typesArray.indexOf(DataTransfers.FILES.toLowerCase()) === -1 && typesArray.indexOf(CodeDataTransfers.FILES.toLowerCase()) === -1) { -// return DRAG_OVER_REJECT; -// } -// } - -// // Other-Tree DND -// else if (data instanceof ExternalElementsDragAndDropData) { -// return DRAG_OVER_REJECT; -// } - -// // In-Explorer DND -// else { -// const sources: ExplorerItem[] = data.getData(); -// if (target instanceof Model) { -// if (sources[0].isRoot) { -// return DRAG_OVER_ACCEPT_BUBBLE_DOWN(false); -// } - -// return DRAG_OVER_REJECT; -// } - -// if (!Array.isArray(sources)) { -// return DRAG_OVER_REJECT; -// } - -// if (sources.some((source) => { -// if (source instanceof NewStatPlaceholder) { -// return true; // NewStatPlaceholders can not be moved -// } - -// if (source.isRoot && target instanceof ExplorerItem && !target.isRoot) { -// return true; // Root folder can not be moved to a non root file stat. -// } - -// if (source.resource.toString() === target.resource.toString()) { -// return true; // Can not move anything onto itself -// } - -// if (source.isRoot && target instanceof ExplorerItem && target.isRoot) { -// // Disable moving workspace roots in one another -// return false; -// } - -// if (!isCopy && resources.dirname(source.resource).toString() === target.resource.toString()) { -// return true; // Can not move a file to the same parent unless we copy -// } - -// if (resources.isEqualOrParent(target.resource, source.resource, !isLinux /* ignorecase */)) { -// return true; // Can not move a parent folder into one of its children -// } - -// return false; -// })) { -// return DRAG_OVER_REJECT; -// } -// } - -// // All (target = model) -// if (target instanceof Model) { -// return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(false) : DRAG_OVER_REJECT; // can only drop folders to workspace -// } - -// // All (target = file/folder) -// else { -// if (target.isDirectory) { -// if (target.isReadonly) { -// return DRAG_OVER_REJECT; -// } -// return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY(true) : DRAG_OVER_ACCEPT_BUBBLE_DOWN(true); -// } - -// if (this.contextService.getWorkspace().folders.every(folder => folder.uri.toString() !== target.resource.toString())) { -// return fromDesktop || isCopy ? DRAG_OVER_ACCEPT_BUBBLE_UP_COPY : DRAG_OVER_ACCEPT_BUBBLE_UP; -// } -// } - -// return DRAG_OVER_REJECT; -// } - -// public drop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): void { - -// // Desktop DND (Import file) -// if (data instanceof DesktopDragAndDropData) { -// this.handleExternalDrop(tree, data, target, originalEvent); -// } - -// // In-Explorer DND (Move/Copy file) -// else { -// this.handleExplorerDrop(tree, data, target, originalEvent); -// } -// } - -// private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise { -// const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); - -// // Check for dropped external files to be folders -// return this.fileService.resolveFiles(droppedResources).then(result => { - -// // Pass focus to window -// this.windowService.focusWindow(); - -// // Handle folders by adding to workspace if we are in workspace context -// const folders = result.filter(r => r.success && r.stat.isDirectory).map(result => ({ uri: result.stat.resource })); -// if (folders.length > 0) { - -// // If we are in no-workspace context, ask for confirmation to create a workspace -// let confirmedPromise: Promise = Promise.resolve({ confirmed: true }); -// if (this.contextService.getWorkbenchState() !== WorkbenchState.WORKSPACE) { -// confirmedPromise = this.dialogService.confirm({ -// message: folders.length > 1 ? nls.localize('dropFolders', "Do you want to add the folders to the workspace?") : nls.localize('dropFolder', "Do you want to add the folder to the workspace?"), -// type: 'question', -// primaryButton: folders.length > 1 ? nls.localize('addFolders', "&&Add Folders") : nls.localize('addFolder', "&&Add Folder") -// }); -// } - -// return confirmedPromise.then(res => { -// if (res.confirmed) { -// return this.workspaceEditingService.addFolders(folders); -// } - -// return undefined; -// }); -// } - -// // Handle dropped files (only support FileStat as target) -// else if (target instanceof ExplorerItem && !target.isReadonly) { -// const addFilesAction = this.instantiationService.createInstance(AddFilesAction, tree, target, null); - -// return addFilesAction.run(droppedResources.map(res => res.resource)); -// } - -// return undefined; -// }); -// } - -// private handleExplorerDrop(tree: ITree, data: IDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise { -// const sources: ExplorerItem[] = resources.distinctParents(data.getData(), s => s.resource); -// const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); - -// let confirmPromise: Promise; - -// // Handle confirm setting -// const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); -// if (confirmDragAndDrop) { -// confirmPromise = this.dialogService.confirm({ -// message: sources.length > 1 && sources.every(s => s.isRoot) ? nls.localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") -// : sources.length > 1 ? getConfirmMessage(nls.localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", sources.length), sources.map(s => s.resource)) -// : sources[0].isRoot ? nls.localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", sources[0].name) -// : nls.localize('confirmMove', "Are you sure you want to move '{0}'?", sources[0].name), -// checkbox: { -// label: nls.localize('doNotAskAgain', "Do not ask me again") -// }, -// type: 'question', -// primaryButton: nls.localize({ key: 'moveButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Move") -// }); -// } else { -// confirmPromise = Promise.resolve({ confirmed: true } as IConfirmationResult); -// } - -// return confirmPromise.then(res => { - -// // Check for confirmation checkbox -// let updateConfirmSettingsPromise: Promise = Promise.resolve(undefined); -// if (res.confirmed && res.checkboxChecked === true) { -// updateConfirmSettingsPromise = this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); -// } - -// return updateConfirmSettingsPromise.then(() => { -// if (res.confirmed) { -// const rootDropPromise = this.doHandleRootDrop(sources.filter(s => s.isRoot), target); -// return Promise.all(sources.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(tree, source, target, isCopy)).concat(rootDropPromise)).then(() => undefined); -// } - -// return Promise.resolve(undefined); -// }); -// }); -// } - -// private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem | Model): Promise { -// if (roots.length === 0) { -// return Promise.resolve(undefined); -// } - -// const folders = this.contextService.getWorkspace().folders; -// let targetIndex: number; -// const workspaceCreationData: IWorkspaceFolderCreationData[] = []; -// const rootsToMove: IWorkspaceFolderCreationData[] = []; - -// for (let index = 0; index < folders.length; index++) { -// const data = { -// uri: folders[index].uri -// }; -// if (target instanceof ExplorerItem && folders[index].uri.toString() === target.resource.toString()) { -// targetIndex = workspaceCreationData.length; -// } - -// if (roots.every(r => r.resource.toString() !== folders[index].uri.toString())) { -// workspaceCreationData.push(data); -// } else { -// rootsToMove.push(data); -// } -// } -// if (target instanceof Model) { -// targetIndex = workspaceCreationData.length; -// } - -// workspaceCreationData.splice(targetIndex, 0, ...rootsToMove); -// return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); -// } - -// private doHandleExplorerDrop(tree: ITree, source: ExplorerItem, target: ExplorerItem | Model, isCopy: boolean): Promise { -// if (!(target instanceof ExplorerItem)) { -// return Promise.resolve(undefined); -// } - -// return tree.expand(target).then(() => { - -// if (target.isReadonly) { -// return undefined; -// } - -// // Reuse duplicate action if user copies -// if (isCopy) { -// return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run(); -// } - -// // Otherwise move -// const targetResource = resources.joinPath(target.resource, source.name); - -// return this.textFileService.move(source.resource, targetResource).then(undefined, error => { - -// // Conflict -// if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { -// const confirm: IConfirmation = { -// message: nls.localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), -// detail: nls.localize('irreversible', "This action is irreversible!"), -// primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), -// type: 'warning' -// }; - -// // Move with overwrite if the user confirms -// return this.dialogService.confirm(confirm).then(res => { -// if (res.confirmed) { -// return this.textFileService.move(source.resource, targetResource, true /* overwrite */).then(undefined, error => this.notificationService.error(error)); -// } - -// return undefined; -// }); -// } - -// // Any other error -// else { -// this.notificationService.error(error); -// } - -// return undefined; -// }); -// }, errors.onUnexpectedError); -// } -// } +export class FileDragAndDrop implements ITreeDragAndDrop { + private static readonly CONFIRM_DND_SETTING_KEY = 'explorer.confirmDragAndDrop'; + + private toDispose: IDisposable[]; + private dropEnabled: boolean; + + constructor( + @INotificationService private notificationService: INotificationService, + @IExplorerService private explorerService: IExplorerService, + @IEditorService private editorService: IEditorService, + @IDialogService private dialogService: IDialogService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IFileService private fileService: IFileService, + @IConfigurationService private configurationService: IConfigurationService, + @IInstantiationService private instantiationService: IInstantiationService, + @ITextFileService private textFileService: ITextFileService, + @IWindowService private windowService: IWindowService, + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + ) { + this.toDispose = []; + + const updateDropEnablement = () => { + this.dropEnabled = this.configurationService.getValue('explorer.enableDragAndDrop'); + }; + updateDropEnablement(); + this.toDispose.push(this.configurationService.onDidChangeConfiguration((e) => updateDropEnablement())); + } + + onDragOver(data: IDragAndDropData, target: ExplorerItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + if (!this.dropEnabled) { + return false; + } + + const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); + const fromDesktop = data instanceof DesktopDragAndDropData; + + // Desktop DND + if (fromDesktop) { + const types = originalEvent.dataTransfer.types; + const typesArray: string[] = []; + for (let i = 0; i < types.length; i++) { + typesArray.push(types[i].toLowerCase()); // somehow the types are lowercase + } + + if (typesArray.indexOf(DataTransfers.FILES.toLowerCase()) === -1 && typesArray.indexOf(CodeDataTransfers.FILES.toLowerCase()) === -1) { + return false; + } + } + + // Other-Tree DND + else if (data instanceof ExternalElementsDragAndDropData) { + return false; + } + + // In-Explorer DND + else { + const sources: ExplorerItem[] = data.getData(); + if (!target) { + if (sources[0].isRoot) { + return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; + } + + return false; + } + + if (!Array.isArray(sources)) { + return false; + } + + if (sources.some((source) => { + if (source.isRoot && target instanceof ExplorerItem && !target.isRoot) { + return true; // Root folder can not be moved to a non root file stat. + } + + if (source.resource.toString() === target.resource.toString()) { + return true; // Can not move anything onto itself + } + + if (source.isRoot && target instanceof ExplorerItem && target.isRoot) { + // Disable moving workspace roots in one another + return false; + } + + if (!isCopy && dirname(source.resource).toString() === target.resource.toString()) { + return true; // Can not move a file to the same parent unless we copy + } + + if (isEqualOrParent(target.resource, source.resource, !isLinux /* ignorecase */)) { + return true; // Can not move a parent folder into one of its children + } + + return false; + })) { + return false; + } + } + + // All (target = model) + if (!target) { + return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? { accept: true, bubble: TreeDragOverBubble.Down } : false; // can only drop folders to workspace + } + + // All (target = file/folder) + else { + const effect = fromDesktop || isCopy ? ListDragOverEffect.Copy : ListDragOverEffect.Move; + + if (target.isDirectory) { + if (target.isReadonly) { + return false; + } + + return { accept: true, bubble: TreeDragOverBubble.Down, effect, autoExpand: true }; + } + + if (this.contextService.getWorkspace().folders.every(folder => folder.uri.toString() !== target.resource.toString())) { + return { accept: true, bubble: TreeDragOverBubble.Up, effect }; + } + } + + return false; + } + + getDragURI(element: ExplorerItem): string { + return element.resource.toString(); + } + + getDragLabel(elements: ExplorerItem[]): string { + if (elements.length > 1) { + return String(elements.length); + } + + return elements[0].name; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + const sources: ExplorerItem[] = data.getData(); + if (sources && sources.length) { + // Apply some datatransfer types to allow for dragging the element outside of the application + this.instantiationService.invokeFunction(fillResourceDataTransfers, sources, originalEvent); + + // The only custom data transfer we set from the explorer is a file transfer + // to be able to DND between multiple code file explorers across windows + const fileResources = sources.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); + if (fileResources.length) { + originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); + } + } + } + + drop(data: IDragAndDropData, target: ExplorerItem, targetIndex: number, originalEvent: DragEvent): void { + // Desktop DND (Import file) + if (data instanceof DesktopDragAndDropData) { + this.handleExternalDrop(data, target, originalEvent); + } + + // In-Explorer DND (Move/Copy file) + else { + this.handleExplorerDrop(data, target, originalEvent); + } + } + + + private handleExternalDrop(data: DesktopDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { + const droppedResources = extractResources(originalEvent, true); + + // Check for dropped external files to be folders + return this.fileService.resolveFiles(droppedResources).then(result => { + + // Pass focus to window + this.windowService.focusWindow(); + + // Handle folders by adding to workspace if we are in workspace context + const folders = result.filter(r => r.success && r.stat.isDirectory).map(result => ({ uri: result.stat.resource })); + if (folders.length > 0) { + + // If we are in no-workspace context, ask for confirmation to create a workspace + let confirmedPromise: Promise = Promise.resolve({ confirmed: true }); + if (this.contextService.getWorkbenchState() !== WorkbenchState.WORKSPACE) { + confirmedPromise = this.dialogService.confirm({ + message: folders.length > 1 ? localize('dropFolders', "Do you want to add the folders to the workspace?") : localize('dropFolder', "Do you want to add the folder to the workspace?"), + type: 'question', + primaryButton: folders.length > 1 ? localize('addFolders', "&&Add Folders") : localize('addFolder', "&&Add Folder") + }); + } + + return confirmedPromise.then(res => { + if (res.confirmed) { + return this.workspaceEditingService.addFolders(folders); + } + + return undefined; + }); + } + + // Handle dropped files (only support FileStat as target) + else if (target instanceof ExplorerItem && !target.isReadonly) { + return this.addResources(target, droppedResources.map(res => res.resource)); + } + + return undefined; + }); + } + + private addResources(target: ExplorerItem, resources: URI[]): Promise { + if (resources && resources.length > 0) { + + // Find parent to add to + if (!target) { + target = this.explorerService.roots[0]; + } + + if (!target.isDirectory) { + target = target.parent; + } + + // Resolve target to check for name collisions and ask user + return this.fileService.resolveFile(target.resource).then((targetStat: IFileStat) => { + + // Check for name collisions + const targetNames = new Set(); + targetStat.children.forEach((child) => { + targetNames.add(isLinux ? child.name : child.name.toLowerCase()); + }); + + let overwritePromise: Promise = Promise.resolve({ confirmed: true }); + if (resources.some(resource => { + return targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase()); + })) { + const confirm: IConfirmation = { + message: localize('confirmOverwrite', "A file or folder with the same name already exists in the destination folder. Do you want to replace it?"), + detail: localize('irreversible', "This action is irreversible!"), + primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + + overwritePromise = this.dialogService.confirm(confirm); + } + + return overwritePromise.then(res => { + if (!res.confirmed) { + return undefined; + } + + // Run add in sequence + const addPromisesFactory: ITask>[] = []; + resources.forEach(resource => { + addPromisesFactory.push(() => { + const sourceFile = resource; + const targetFile = joinPath(target.resource, basename(sourceFile)); + + // if the target exists and is dirty, make sure to revert it. otherwise the dirty contents + // of the target file would replace the contents of the added file. since we already + // confirmed the overwrite before, this is OK. + let revertPromise: Promise = Promise.resolve(null); + if (this.textFileService.isDirty(targetFile)) { + revertPromise = this.textFileService.revertAll([targetFile], { soft: true }); + } + + return revertPromise.then(() => { + const copyTarget = joinPath(target.resource, basename(sourceFile)); + return this.fileService.copyFile(sourceFile, copyTarget, true).then(stat => { + + // if we only add one file, just open it directly + if (resources.length === 1) { + this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + } + }); + }); + }); + }); + + return sequence(addPromisesFactory); + }); + }); + } + + return Promise.resolve(undefined); + } + + private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem | undefined, originalEvent: DragEvent): Promise { + const sources: ExplorerItem[] = distinctParents(data.getData(), s => s.resource); + const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); + + let confirmPromise: Promise; + + // Handle confirm setting + const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); + if (confirmDragAndDrop) { + confirmPromise = this.dialogService.confirm({ + message: sources.length > 1 && sources.every(s => s.isRoot) ? localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") + : sources.length > 1 ? getConfirmMessage(localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", sources.length), sources.map(s => s.resource)) + : sources[0].isRoot ? localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", sources[0].name) + : localize('confirmMove', "Are you sure you want to move '{0}'?", sources[0].name), + checkbox: { + label: localize('doNotAskAgain', "Do not ask me again") + }, + type: 'question', + primaryButton: localize({ key: 'moveButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Move") + }); + } else { + confirmPromise = Promise.resolve({ confirmed: true } as IConfirmationResult); + } + + return confirmPromise.then(res => { + + // Check for confirmation checkbox + let updateConfirmSettingsPromise: Promise = Promise.resolve(undefined); + if (res.confirmed && res.checkboxChecked === true) { + updateConfirmSettingsPromise = this.configurationService.updateValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY, false, ConfigurationTarget.USER); + } + + return updateConfirmSettingsPromise.then(() => { + if (res.confirmed) { + const rootDropPromise = this.doHandleRootDrop(sources.filter(s => s.isRoot), target); + return Promise.all(sources.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, isCopy)).concat(rootDropPromise)).then(() => undefined); + } + + return Promise.resolve(undefined); + }); + }); + } + + private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem | undefined): Promise { + if (roots.length === 0) { + return Promise.resolve(undefined); + } + + const folders = this.contextService.getWorkspace().folders; + let targetIndex: number; + const workspaceCreationData: IWorkspaceFolderCreationData[] = []; + const rootsToMove: IWorkspaceFolderCreationData[] = []; + + for (let index = 0; index < folders.length; index++) { + const data = { + uri: folders[index].uri + }; + if (target instanceof ExplorerItem && folders[index].uri.toString() === target.resource.toString()) { + targetIndex = workspaceCreationData.length; + } + + if (roots.every(r => r.resource.toString() !== folders[index].uri.toString())) { + workspaceCreationData.push(data); + } else { + rootsToMove.push(data); + } + } + if (!target) { + // Empty area + targetIndex = workspaceCreationData.length; + } + + workspaceCreationData.splice(targetIndex, 0, ...rootsToMove); + return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); + } + + private doHandleExplorerDrop(source: ExplorerItem, target: ExplorerItem | undefined, isCopy: boolean): Promise { + if (!(target instanceof ExplorerItem)) { + return Promise.resolve(undefined); + } + + if (target.isReadonly) { + return undefined; + } + + // Reuse duplicate action if user copies + if (isCopy) { + + return this.fileService.copyFile(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory })).then(stat => { + if (!stat.isDirectory) { + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }).then(() => undefined); + } + + return undefined; + }); + } + + // Otherwise move + const targetResource = joinPath(target.resource, source.name); + + return this.textFileService.move(source.resource, targetResource).then(undefined, error => { + + // Conflict + if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { + const confirm: IConfirmation = { + message: localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), + detail: localize('irreversible', "This action is irreversible!"), + primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), + type: 'warning' + }; + + // Move with overwrite if the user confirms + return this.dialogService.confirm(confirm).then(res => { + if (res.confirmed) { + return this.textFileService.move(source.resource, targetResource, true /* overwrite */).then(undefined, error => this.notificationService.error(error)); + } + + return undefined; + }); + } + + // Any other error + else { + this.notificationService.error(error); + } + + return undefined; + }); + } +} From bfe8ab5b09a7e42a15d899267e2558eb0f98dce6 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 15:59:58 +0100 Subject: [PATCH 0153/1066] explorer: drag and drop polish --- .../electron-browser/watchExpressionsView.ts | 6 ++-- .../electron-browser/views/explorerViewer.ts | 34 ++++++++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 581cfda36f7..ce2a21d53f5 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -290,10 +290,10 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { } drop(data: IDragAndDropData, targetElement: IExpression): void { - const draggedData = data.getData(); + const expressions = (data as ElementsDragAndDropData).elements; - if (Array.isArray(draggedData)) { - const draggedElement = draggedData[0].element.element; + if (Array.isArray(expressions)) { + const draggedElement = expressions[0]; const watches = this.debugService.getModel().getWatchExpressions(); const position = targetElement instanceof Expression ? watches.indexOf(targetElement) : watches.length - 1; this.debugService.moveWatchExpression(draggedElement.getId(), position); 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 fec0dea1640..4aaeadee93f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -35,7 +35,7 @@ import { fillResourceDataTransfers, CodeDataTransfers, extractResources } from ' import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { Schemas } from 'vs/base/common/network'; -import { DesktopDragAndDropData, ExternalElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { DesktopDragAndDropData, ExternalElementsDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isMacintosh, isLinux } from 'vs/base/common/platform'; import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; @@ -450,20 +450,21 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // In-Explorer DND else { - const sources: ExplorerItem[] = data.getData(); + const items = (data as ElementsDragAndDropData).elements; + if (!target) { - if (sources[0].isRoot) { + if (items[0].isRoot) { return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; } return false; } - if (!Array.isArray(sources)) { + if (!Array.isArray(items)) { return false; } - if (sources.some((source) => { + if (items.some((source) => { if (source.isRoot && target instanceof ExplorerItem && !target.isRoot) { return true; // Root folder can not be moved to a non root file stat. } @@ -529,14 +530,14 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { - const sources: ExplorerItem[] = data.getData(); - if (sources && sources.length) { + const items = (data as ElementsDragAndDropData).elements; + if (items && items.length) { // Apply some datatransfer types to allow for dragging the element outside of the application - this.instantiationService.invokeFunction(fillResourceDataTransfers, sources, originalEvent); + this.instantiationService.invokeFunction(fillResourceDataTransfers, items, originalEvent); // The only custom data transfer we set from the explorer is a file transfer // to be able to DND between multiple code file explorers across windows - const fileResources = sources.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); + const fileResources = items.filter(s => !s.isDirectory && s.resource.scheme === Schemas.file).map(r => r.resource.fsPath); if (fileResources.length) { originalEvent.dataTransfer.setData(CodeDataTransfers.FILES, JSON.stringify(fileResources)); } @@ -674,7 +675,8 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem | undefined, originalEvent: DragEvent): Promise { - const sources: ExplorerItem[] = distinctParents(data.getData(), s => s.resource); + const elementsData = (data as ElementsDragAndDropData).elements; + const items = distinctParents(elementsData, s => s.resource); const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); let confirmPromise: Promise; @@ -683,10 +685,10 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const confirmDragAndDrop = !isCopy && this.configurationService.getValue(FileDragAndDrop.CONFIRM_DND_SETTING_KEY); if (confirmDragAndDrop) { confirmPromise = this.dialogService.confirm({ - message: sources.length > 1 && sources.every(s => s.isRoot) ? localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") - : sources.length > 1 ? getConfirmMessage(localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", sources.length), sources.map(s => s.resource)) - : sources[0].isRoot ? localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", sources[0].name) - : localize('confirmMove', "Are you sure you want to move '{0}'?", sources[0].name), + message: items.length > 1 && items.every(s => s.isRoot) ? localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?") + : items.length > 1 ? getConfirmMessage(localize('confirmMultiMove', "Are you sure you want to move the following {0} files?", items.length), items.map(s => s.resource)) + : items[0].isRoot ? localize('confirmRootMove', "Are you sure you want to change the order of root folder '{0}' in your workspace?", items[0].name) + : localize('confirmMove', "Are you sure you want to move '{0}'?", items[0].name), checkbox: { label: localize('doNotAskAgain', "Do not ask me again") }, @@ -707,8 +709,8 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return updateConfirmSettingsPromise.then(() => { if (res.confirmed) { - const rootDropPromise = this.doHandleRootDrop(sources.filter(s => s.isRoot), target); - return Promise.all(sources.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, isCopy)).concat(rootDropPromise)).then(() => undefined); + const rootDropPromise = this.doHandleRootDrop(items.filter(s => s.isRoot), target); + return Promise.all(items.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, isCopy)).concat(rootDropPromise)).then(() => undefined); } return Promise.resolve(undefined); From 05708fd66c7b7a6c47231141cc8ef0351df2578c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Jan 2019 17:19:57 +0100 Subject: [PATCH 0154/1066] storage - add test for recovery --- src/vs/base/test/node/storage/storage.test.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 11384986e9b..9d4b5085429 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -246,6 +246,36 @@ suite('Storage Library', () => { await storage.close(); await del(storageDir, tmpdir()); }); + + test('corrupt DB recovers', async () => { + const storageDir = uniqueStorageDir(); + await mkdirp(storageDir); + + const storageFile = join(storageDir, 'storage.db'); + + let storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + await storage.set('bar', 'foo'); + + await writeFile(storageFile, 'This is a broken DB'); + + await storage.set('foo', 'bar'); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + + await storage.close(); + + storage = new Storage(new SQLiteStorageDatabase(storageFile)); + await storage.init(); + + equal(storage.get('bar'), 'foo'); + equal(storage.get('foo'), 'bar'); + + await storage.close(); + await del(storageDir, tmpdir()); + }); }); suite('SQLite Storage Library', () => { From 6ac72bd304af13767655de68a761dfd09cd234d3 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 16:25:43 +0100 Subject: [PATCH 0155/1066] label: minor rename --- src/vs/editor/standalone/browser/simpleServices.ts | 6 +++--- src/vs/platform/label/common/label.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 42fa70cee9b..f083e2ce44c 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -36,7 +36,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ILabelService, LabelRules, RegisterFormatterEvent } from 'vs/platform/label/common/label'; +import { ILabelService, LabelRules, RegisterFormatterData } from 'vs/platform/label/common/label'; import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -612,8 +612,8 @@ export class SimpleBulkEditService implements IBulkEditService { export class SimpleUriLabelService implements ILabelService { _serviceBrand: any; - private readonly _onDidRegisterFormatter = new Emitter(); - public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; + private readonly _onDidRegisterFormatter = new Emitter(); + public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; public getUriLabel(resource: URI, options?: { relative?: boolean, forceNoTildify?: boolean }): string { if (resource.scheme === 'file') { diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 7532fc55e9c..bb0e9f13184 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -21,7 +21,7 @@ import { Schemas } from 'vs/base/common/network'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -export interface RegisterFormatterEvent { +export interface RegisterFormatterData { selector: string; formatter: LabelRules; } @@ -37,7 +37,7 @@ export interface ILabelService { getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(): string; registerFormatter(selector: string, formatter: LabelRules): IDisposable; - onDidRegisterFormatter: Event; + onDidRegisterFormatter: Event; } export interface LabelRules { @@ -79,7 +79,7 @@ export class LabelService implements ILabelService { _serviceBrand: any; private readonly formatters: { [prefix: string]: LabelRules } = Object.create(null); - private readonly _onDidRegisterFormatter = new Emitter(); + private readonly _onDidRegisterFormatter = new Emitter(); constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -87,7 +87,7 @@ export class LabelService implements ILabelService { @IWindowService private readonly windowService: IWindowService ) { } - get onDidRegisterFormatter(): Event { + get onDidRegisterFormatter(): Event { return this._onDidRegisterFormatter.event; } From c47c001600b635cebdca80ce3f762fe5521615ca Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 16:40:50 +0100 Subject: [PATCH 0156/1066] move labelService to workbench --- build/lib/i18n.resources.json | 4 + src/vs/platform/label/common/label.ts | 169 +---------------- src/vs/workbench/electron-browser/shell.ts | 3 +- .../services/label/common/labelService.ts | 176 ++++++++++++++++++ .../services}/label/test/label.test.ts | 2 +- .../api/mainThreadEditors.test.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 3 +- 7 files changed, 192 insertions(+), 167 deletions(-) create mode 100644 src/vs/workbench/services/label/common/labelService.ts rename src/vs/{platform => workbench/services}/label/test/label.test.ts (96%) diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 296d1286fbb..f8a57d73f6b 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -238,6 +238,10 @@ "name": "vs/workbench/services/decorations", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/label", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/preferences", "project": "vscode-preferences" diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index bb0e9f13184..8de1893da38 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -5,21 +5,15 @@ import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { Event } from 'vs/base/common/event'; +import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; -import { isLinux, isWindows } from 'vs/base/common/platform'; -import { tildify, getPathLabel } from 'vs/base/common/labels'; -import { ltrim, startsWith } from 'vs/base/common/strings'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { basenameOrAuthority } from 'vs/base/common/resources'; +import { isLinux } from 'vs/base/common/platform'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; import { localize } from 'vs/nls'; import { isParent } from 'vs/platform/files/common/files'; -import { basename, dirname, join } from 'vs/base/common/paths'; -import { Schemas } from 'vs/base/common/network'; -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { basename } from 'vs/base/common/paths'; export interface RegisterFormatterData { selector: string; @@ -54,12 +48,6 @@ export interface LabelRules { } const LABEL_SERVICE_ID = 'label'; -const sepRegexp = /\//g; -const labelMatchingRegexp = /\$\{scheme\}|\$\{authority\}|\$\{path\}/g; - -function hasDriveLetter(path: string): boolean { - return !!(isWindows && path && path[2] === ':'); -} export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, workspaceHome: string): string { if (isSingleFolderWorkspaceIdentifier(workspace)) { @@ -75,150 +63,5 @@ export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, w return localize('workspaceName', "{0} (Workspace)", workspaceName); } -export class LabelService implements ILabelService { - _serviceBrand: any; - - private readonly formatters: { [prefix: string]: LabelRules } = Object.create(null); - private readonly _onDidRegisterFormatter = new Emitter(); - - constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IWindowService private readonly windowService: IWindowService - ) { } - - get onDidRegisterFormatter(): Event { - return this._onDidRegisterFormatter.event; - } - - findFormatter(resource: URI): LabelRules | undefined { - const path = `${resource.scheme}://${resource.authority}`; - let bestPrefix = ''; - for (let prefix in this.formatters) { - if (startsWith(path, prefix) && prefix.length > bestPrefix.length) { - bestPrefix = prefix; - } - } - if (bestPrefix.length) { - return this.formatters[bestPrefix]; - } - return undefined; - } - - getUriLabel(resource: URI, options: { relative?: boolean, noPrefix?: boolean } = {}): string { - const formatter = this.findFormatter(resource); - if (!formatter) { - return getPathLabel(resource.path, this.environmentService, options.relative ? this.contextService : undefined); - } - - if (options.relative) { - const baseResource = this.contextService && this.contextService.getWorkspaceFolder(resource); - if (baseResource) { - let relativeLabel: string; - if (isEqual(baseResource.uri, resource, !isLinux)) { - relativeLabel = ''; // no label if resources are identical - } else { - const baseResourceLabel = this.formatUri(baseResource.uri, formatter, options.noPrefix); - relativeLabel = ltrim(this.formatUri(resource, formatter, options.noPrefix).substring(baseResourceLabel.length), formatter.uri.separator); - } - - const hasMultipleRoots = this.contextService.getWorkspace().folders.length > 1; - if (hasMultipleRoots && !options.noPrefix) { - const rootName = (baseResource && baseResource.name) ? baseResource.name : basenameOrAuthority(baseResource.uri); - relativeLabel = relativeLabel ? (rootName + ' • ' + relativeLabel) : rootName; // always show root basename if there are multiple - } - - return relativeLabel; - } - } - - return this.formatUri(resource, formatter, options.noPrefix); - } - - getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { - if (!isWorkspaceIdentifier(workspace) && !isSingleFolderWorkspaceIdentifier(workspace)) { - const identifier = toWorkspaceIdentifier(workspace); - if (!identifier) { - return ''; - } - - workspace = identifier; - } - - // Workspace: Single Folder - if (isSingleFolderWorkspaceIdentifier(workspace)) { - // Folder on disk - const formatter = this.findFormatter(workspace); - const label = options && options.verbose ? this.getUriLabel(workspace) : basenameOrAuthority(workspace); - if (workspace.scheme === Schemas.file) { - return label; - } - - const suffix = formatter && formatter.workspace && (typeof formatter.workspace.suffix === 'string') ? formatter.workspace.suffix : workspace.scheme; - return suffix ? `${label} (${suffix})` : label; - } - - // Workspace: Untitled - if (isParent(workspace.configPath, this.environmentService.workspacesHome, !isLinux /* ignore case */)) { - return localize('untitledWorkspace', "Untitled (Workspace)"); - } - - // Workspace: Saved - const filename = basename(workspace.configPath); - const workspaceName = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); - if (options && options.verbose) { - return localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(URI.file(join(dirname(workspace.configPath), workspaceName)))); - } - - return localize('workspaceName', "{0} (Workspace)", workspaceName); - } - - getHostLabel(): string { - if (this.windowService) { - const authority = this.windowService.getConfiguration().remoteAuthority; - if (authority) { - const formatter = this.findFormatter(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); - if (formatter && formatter.workspace) { - return formatter.workspace.suffix; - } - } - } - return ''; - } - - registerFormatter(selector: string, formatter: LabelRules): IDisposable { - this.formatters[selector] = formatter; - this._onDidRegisterFormatter.fire({ selector, formatter }); - - return { - dispose: () => delete this.formatters[selector] - }; - } - - private formatUri(resource: URI, formatter: LabelRules, forceNoTildify?: boolean): string { - let label = formatter.uri.label.replace(labelMatchingRegexp, match => { - switch (match) { - case '${scheme}': return resource.scheme; - case '${authority}': return resource.authority; - case '${path}': return resource.path; - default: return ''; - } - }); - - // convert \c:\something => C:\something - if (formatter.uri.normalizeDriveLetter && hasDriveLetter(label)) { - label = label.charAt(1).toUpperCase() + label.substr(2); - } - - if (formatter.uri.tildify && !forceNoTildify) { - label = tildify(label, this.environmentService.userHome); - } - if (formatter.uri.authorityPrefix && resource.authority) { - label = formatter.uri.authorityPrefix + label; - } - - return label.replace(sepRegexp, formatter.uri.separator); - } -} export const ILabelService = createDecorator(LABEL_SERVICE_ID); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 564fad93637..3b28eced7e7 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -96,7 +96,7 @@ import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHi import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { LogLevelSetterChannel } from 'vs/platform/log/node/logIpc'; -import { ILabelService, LabelService } from 'vs/platform/label/common/label'; +import { ILabelService } from 'vs/platform/label/common/label'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/node/downloadService'; import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; @@ -106,6 +106,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-browser/remoteAuthorityResolverService'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; import { MarkerDecorationsService } from 'vs/editor/common/services/markerDecorationsServiceImpl'; +import { LabelService } from 'vs/workbench/services/label/common/labelService'; /** * Services that we require for the Shell diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts new file mode 100644 index 00000000000..146c7bd4b9d --- /dev/null +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -0,0 +1,176 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from 'vs/nls'; +import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; +import { isLinux, isWindows } from 'vs/base/common/platform'; +import { tildify, getPathLabel } from 'vs/base/common/labels'; +import { ltrim, startsWith } from 'vs/base/common/strings'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { isParent } from 'vs/platform/files/common/files'; +import { basename, dirname, join } from 'vs/base/common/paths'; +import { Schemas } from 'vs/base/common/network'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; +import { ILabelService, LabelRules, RegisterFormatterData } from 'vs/platform/label/common/label'; + +const sepRegexp = /\//g; +const labelMatchingRegexp = /\$\{scheme\}|\$\{authority\}|\$\{path\}/g; + +function hasDriveLetter(path: string): boolean { + return !!(isWindows && path && path[2] === ':'); +} + + +export class LabelService implements ILabelService { + _serviceBrand: any; + + private readonly formatters: { [prefix: string]: LabelRules } = Object.create(null); + private readonly _onDidRegisterFormatter = new Emitter(); + + constructor( + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IWindowService private readonly windowService: IWindowService + ) { } + + get onDidRegisterFormatter(): Event { + return this._onDidRegisterFormatter.event; + } + + findFormatter(resource: URI): LabelRules | undefined { + const path = `${resource.scheme}://${resource.authority}`; + let bestPrefix = ''; + for (let prefix in this.formatters) { + if (startsWith(path, prefix) && prefix.length > bestPrefix.length) { + bestPrefix = prefix; + } + } + if (bestPrefix.length) { + return this.formatters[bestPrefix]; + } + return undefined; + } + + getUriLabel(resource: URI, options: { relative?: boolean, noPrefix?: boolean } = {}): string { + const formatter = this.findFormatter(resource); + if (!formatter) { + return getPathLabel(resource.path, this.environmentService, options.relative ? this.contextService : undefined); + } + + if (options.relative) { + const baseResource = this.contextService && this.contextService.getWorkspaceFolder(resource); + if (baseResource) { + let relativeLabel: string; + if (isEqual(baseResource.uri, resource, !isLinux)) { + relativeLabel = ''; // no label if resources are identical + } else { + const baseResourceLabel = this.formatUri(baseResource.uri, formatter, options.noPrefix); + relativeLabel = ltrim(this.formatUri(resource, formatter, options.noPrefix).substring(baseResourceLabel.length), formatter.uri.separator); + } + + const hasMultipleRoots = this.contextService.getWorkspace().folders.length > 1; + if (hasMultipleRoots && !options.noPrefix) { + const rootName = (baseResource && baseResource.name) ? baseResource.name : basenameOrAuthority(baseResource.uri); + relativeLabel = relativeLabel ? (rootName + ' • ' + relativeLabel) : rootName; // always show root basename if there are multiple + } + + return relativeLabel; + } + } + + return this.formatUri(resource, formatter, options.noPrefix); + } + + getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { + if (!isWorkspaceIdentifier(workspace) && !isSingleFolderWorkspaceIdentifier(workspace)) { + const identifier = toWorkspaceIdentifier(workspace); + if (!identifier) { + return ''; + } + + workspace = identifier; + } + + // Workspace: Single Folder + if (isSingleFolderWorkspaceIdentifier(workspace)) { + // Folder on disk + const formatter = this.findFormatter(workspace); + const label = options && options.verbose ? this.getUriLabel(workspace) : basenameOrAuthority(workspace); + if (workspace.scheme === Schemas.file) { + return label; + } + + const suffix = formatter && formatter.workspace && (typeof formatter.workspace.suffix === 'string') ? formatter.workspace.suffix : workspace.scheme; + return suffix ? `${label} (${suffix})` : label; + } + + // Workspace: Untitled + if (isParent(workspace.configPath, this.environmentService.workspacesHome, !isLinux /* ignore case */)) { + return localize('untitledWorkspace', "Untitled (Workspace)"); + } + + // Workspace: Saved + const filename = basename(workspace.configPath); + const workspaceName = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1); + if (options && options.verbose) { + return localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(URI.file(join(dirname(workspace.configPath), workspaceName)))); + } + + return localize('workspaceName', "{0} (Workspace)", workspaceName); + } + + getHostLabel(): string { + if (this.windowService) { + const authority = this.windowService.getConfiguration().remoteAuthority; + if (authority) { + const formatter = this.findFormatter(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); + if (formatter && formatter.workspace) { + return formatter.workspace.suffix; + } + } + } + return ''; + } + + registerFormatter(selector: string, formatter: LabelRules): IDisposable { + this.formatters[selector] = formatter; + this._onDidRegisterFormatter.fire({ selector, formatter }); + + return { + dispose: () => delete this.formatters[selector] + }; + } + + private formatUri(resource: URI, formatter: LabelRules, forceNoTildify?: boolean): string { + let label = formatter.uri.label.replace(labelMatchingRegexp, match => { + switch (match) { + case '${scheme}': return resource.scheme; + case '${authority}': return resource.authority; + case '${path}': return resource.path; + default: return ''; + } + }); + + // convert \c:\something => C:\something + if (formatter.uri.normalizeDriveLetter && hasDriveLetter(label)) { + label = label.charAt(1).toUpperCase() + label.substr(2); + } + + if (formatter.uri.tildify && !forceNoTildify) { + label = tildify(label, this.environmentService.userHome); + } + if (formatter.uri.authorityPrefix && resource.authority) { + label = formatter.uri.authorityPrefix + label; + } + + return label.replace(sepRegexp, formatter.uri.separator); + } +} diff --git a/src/vs/platform/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts similarity index 96% rename from src/vs/platform/label/test/label.test.ts rename to src/vs/workbench/services/label/test/label.test.ts index 542e7563960..f983ba30f34 100644 --- a/src/vs/platform/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { LabelService } from 'vs/platform/label/common/label'; import { TestEnvironmentService, TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { URI } from 'vs/base/common/uri'; import { nativeSep } from 'vs/base/common/paths'; import { isWindows } from 'vs/base/common/platform'; +import { LabelService } from 'vs/workbench/services/label/common/labelService'; suite('URI Label', () => { diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts index 0869f481294..89e4acd7425 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadEditors.test.ts @@ -25,8 +25,8 @@ import { BulkEditService } from 'vs/workbench/services/bulkEdit/electron-browser import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { IReference, ImmortalReference } from 'vs/base/common/lifecycle'; -import { LabelService } from 'vs/platform/label/common/label'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { LabelService } from 'vs/workbench/services/label/common/labelService'; suite('MainThreadEditors', () => { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index d6eefead84a..5f79471a395 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -70,7 +70,7 @@ import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { Dimension } from 'vs/base/browser/dom'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { ILabelService, LabelService } from 'vs/platform/label/common/label'; +import { ILabelService } from 'vs/platform/label/common/label'; import { timeout } from 'vs/base/common/async'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; @@ -79,6 +79,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { LabelService } from 'vs/workbench/services/label/common/labelService'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined); From 362a7b152ca977488b7436a355836bf13b15ddef Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 17:23:10 +0100 Subject: [PATCH 0157/1066] labelService: allow extensions to contribute label formatting rules fixes #66209 --- .../services/label/common/labelService.ts | 62 ++++++++++++++++++- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 146c7bd4b9d..37a705ff3b4 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -20,6 +20,61 @@ import { Schemas } from 'vs/base/common/network'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { ILabelService, LabelRules, RegisterFormatterData } from 'vs/platform/label/common/label'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; + +interface LabelRulesWithPrefix extends LabelRules { + prefix: string; +} + +export const labelsExtPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'labels', + jsonSchema: { + description: localize('vscode.extension.contributes.labels', 'Contributes label formatting rules.'), + type: 'array', + items: { + type: 'object', + required: ['prefix'], + properties: { + prefix: { + type: 'string', + description: localize('vscode.extension.contributes.labels.prefix', 'URI prefix on which to match the rules on. For example file:///myFolder'), + }, + uri: { + description: localize('vscode.extension.contributes.labels.uri', "Rules for formatting uri resource labels."), + type: 'object', + properties: { + label: { + type: 'string', + description: localize('vscode.extension.contributes.labels.uri.label', "Label rules to display. For example: myLabel:/${path}. ${path}, ${scheme} and ${authority} are supported as variables.") + }, + separator: { + type: 'string', + description: localize('vscode.extension.contributes.labels.uri.separator', "Separator to be used in the uri label display. '/' or '\' as an example.") + }, + tildify: { + type: 'boolean', + description: localize('vscode.extension.contributes.labels.uri.tildify', "Controls if the start of the uri label should be tildified when possible.") + }, + normalizeDriveLetter: { + type: 'boolean', + description: localize('vscode.extension.contributes.labels.uri.normalizeDriveLetter', "Controls if the drive letters should be upper cased.") + } + } + }, + workspace: { + description: localize('vscode.extension.contributes.labels.workspace', "Rules for formatting workspace labels."), + type: 'object', + properties: { + suffix: { + type: 'string', + description: localize('vscode.extension.contributes.labels.workspace.suffix', "Suffix appended to the workspace label.") + } + } + }, + } + } + } +}); const sepRegexp = /\//g; const labelMatchingRegexp = /\$\{scheme\}|\$\{authority\}|\$\{path\}/g; @@ -28,7 +83,6 @@ function hasDriveLetter(path: string): boolean { return !!(isWindows && path && path[2] === ':'); } - export class LabelService implements ILabelService { _serviceBrand: any; @@ -39,7 +93,11 @@ export class LabelService implements ILabelService { @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWindowService private readonly windowService: IWindowService - ) { } + ) { + labelsExtPoint.setHandler(extensions => { + extensions.forEach(extension => extension.value.forEach(rule => this.registerFormatter(rule.prefix, rule))); + }); + } get onDidRegisterFormatter(): Event { return this._onDidRegisterFormatter.event; From d4beb23cb592a29506c1993d2c45b32aea3f55c4 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 10 Jan 2019 17:35:47 +0100 Subject: [PATCH 0158/1066] list: clean drop, dragstart --- src/vs/base/browser/ui/tree/abstractTree.ts | 22 +++--- src/vs/base/browser/ui/tree/asyncDataTree.ts | 75 +++++++++++-------- .../electron-browser/watchExpressionsView.ts | 14 ++-- 3 files changed, 62 insertions(+), 49 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index a38cb342863..31046489dcd 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -17,6 +17,15 @@ import { IDragAndDropData } from 'vs/base/browser/dnd'; import { range } from 'vs/base/common/arrays'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + class TreeNodeListDragAndDrop implements IListDragAndDrop> { private autoExpandNode: ITreeNode | undefined; @@ -38,19 +47,12 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { if (this.dnd.onDragStart) { - this.dnd.onDragStart(data, originalEvent); + this.dnd.onDragStart(asTreeDragAndDropData(data), originalEvent); } } onDragOver(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { - let treeData: IDragAndDropData = data; - - if (data instanceof ElementsDragAndDropData) { - const nodes = (data as ElementsDragAndDropData>).elements; - treeData = new ElementsDragAndDropData(nodes.map(node => node.element)); - } - - const result = this.dnd.onDragOver(treeData, targetNode && targetNode.element, targetIndex, originalEvent); + const result = this.dnd.onDragOver(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); const didChangeAutoExpandNode = this.autoExpandNode !== targetNode; if (didChangeAutoExpandNode) { @@ -102,7 +104,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } drop(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { - this.dnd.drop(data, targetNode && targetNode.element, targetIndex, originalEvent); + this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); } } diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index bead97e19c2..a3ec69680f8 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -5,8 +5,8 @@ import { ComposedTreeDelegate, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; -import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; +import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; +import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { timeout, always } from 'vs/base/common/async'; @@ -124,6 +124,46 @@ export interface IChildrenResolutionEvent { readonly reason: ChildrenResolutionReason; } +function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { + if (data instanceof ElementsDragAndDropData) { + const nodes = (data as ElementsDragAndDropData>).elements; + return new ElementsDragAndDropData(nodes.map(node => node.element)); + } + + return data; +} + +class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop> { + + constructor(private dnd: ITreeDragAndDrop) { } + + getDragURI(node: IAsyncDataTreeNode): string | null { + return this.dnd.getDragURI(node.element as T); + } + + getDragLabel(nodes: IAsyncDataTreeNode[]): string | undefined { + if (this.dnd.getDragLabel) { + return this.dnd.getDragLabel(nodes.map(node => node.element as T)); + } + + return undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + if (this.dnd.onDragStart) { + this.dnd.onDragStart(asAsyncDataTreeDragAndDropData(data), originalEvent); + } + } + + onDragOver(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent, raw = true): boolean | IListDragOverReaction { + return this.dnd.onDragOver(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } + + drop(data: IDragAndDropData, targetNode: IAsyncDataTreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.dnd.drop(asAsyncDataTreeDragAndDropData(data), targetNode && targetNode.element as T, targetIndex, originalEvent); + } +} + function asObjectTreeOptions(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { return options && { ...options, @@ -132,36 +172,7 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt return options.identityProvider!.getId(el.element as T); } }, - dnd: options.dnd && { - getDragURI(node) { - return options.dnd!.getDragURI(node.element as T); - }, - getDragLabel(nodes) { - if (options.dnd!.getDragLabel) { - return options.dnd!.getDragLabel!(nodes.map(node => node.element as T)); - } - - return undefined; - }, - onDragStart(data, originalEvent) { - if (options.dnd!.onDragStart) { - options.dnd!.onDragStart!(data, originalEvent); - } - }, - onDragOver(data, targetNode, targetIndex, originalEvent) { - let treeData: IDragAndDropData = data; - - if (data instanceof ElementsDragAndDropData) { - const nodes = (data as ElementsDragAndDropData>).elements; - treeData = new ElementsDragAndDropData(nodes.map(node => node.element)); - } - - return options.dnd!.onDragOver(treeData, targetNode && targetNode.element as T, targetIndex, originalEvent); - }, - drop(data, targetNode, targetIndex, originalEvent) { - options.dnd!.drop(data, targetNode && targetNode.element as T, targetIndex, originalEvent); - } - }, + dnd: options.dnd && new AsyncDataTreeNodeListDragAndDrop(options.dnd), multipleSelectionController: options.multipleSelectionController && { isSelectionSingleChangeEvent(e) { return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 581cfda36f7..f86593cc33c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -290,13 +290,13 @@ class WatchExpressionsDragAndDrop implements ITreeDragAndDrop { } drop(data: IDragAndDropData, targetElement: IExpression): void { - const draggedData = data.getData(); - - if (Array.isArray(draggedData)) { - const draggedElement = draggedData[0].element.element; - const watches = this.debugService.getModel().getWatchExpressions(); - const position = targetElement instanceof Expression ? watches.indexOf(targetElement) : watches.length - 1; - this.debugService.moveWatchExpression(draggedElement.getId(), position); + if (!(data instanceof ElementsDragAndDropData)) { + return; } + + const draggedElement = (data as ElementsDragAndDropData).elements[0]; + const watches = this.debugService.getModel().getWatchExpressions(); + const position = targetElement instanceof Expression ? watches.indexOf(targetElement) : watches.length - 1; + this.debugService.moveWatchExpression(draggedElement.getId(), position); } } From 498b0d3b386b8027137a50bdcd8916de1c181696 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 17:37:59 +0100 Subject: [PATCH 0159/1066] simplify onDidRegisterFormatter event, it should be void --- src/vs/editor/standalone/browser/simpleServices.ts | 6 +++--- src/vs/platform/label/common/label.ts | 7 +------ .../workbench/services/label/common/labelService.ts | 11 ++++++----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index f083e2ce44c..d8dd50797c2 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -36,7 +36,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ILabelService, LabelRules, RegisterFormatterData } from 'vs/platform/label/common/label'; +import { ILabelService, LabelRules } from 'vs/platform/label/common/label'; import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -612,8 +612,8 @@ export class SimpleBulkEditService implements IBulkEditService { export class SimpleUriLabelService implements ILabelService { _serviceBrand: any; - private readonly _onDidRegisterFormatter = new Emitter(); - public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; + private readonly _onDidRegisterFormatter = new Emitter(); + public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; public getUriLabel(resource: URI, options?: { relative?: boolean, forceNoTildify?: boolean }): string { if (resource.scheme === 'file') { diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 8de1893da38..eec33b6f7b2 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -15,11 +15,6 @@ import { localize } from 'vs/nls'; import { isParent } from 'vs/platform/files/common/files'; import { basename } from 'vs/base/common/paths'; -export interface RegisterFormatterData { - selector: string; - formatter: LabelRules; -} - export interface ILabelService { _serviceBrand: any; /** @@ -31,7 +26,7 @@ export interface ILabelService { getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(): string; registerFormatter(selector: string, formatter: LabelRules): IDisposable; - onDidRegisterFormatter: Event; + onDidRegisterFormatter: Event; } export interface LabelRules { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 37a705ff3b4..4003aba76ab 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -19,7 +19,7 @@ import { basename, dirname, join } from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ILabelService, LabelRules, RegisterFormatterData } from 'vs/platform/label/common/label'; +import { ILabelService, LabelRules } from 'vs/platform/label/common/label'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; interface LabelRulesWithPrefix extends LabelRules { @@ -87,7 +87,7 @@ export class LabelService implements ILabelService { _serviceBrand: any; private readonly formatters: { [prefix: string]: LabelRules } = Object.create(null); - private readonly _onDidRegisterFormatter = new Emitter(); + private readonly _onDidRegisterFormatter = new Emitter(); constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -95,11 +95,12 @@ export class LabelService implements ILabelService { @IWindowService private readonly windowService: IWindowService ) { labelsExtPoint.setHandler(extensions => { - extensions.forEach(extension => extension.value.forEach(rule => this.registerFormatter(rule.prefix, rule))); + extensions.forEach(extension => extension.value.forEach(rule => this.formatters[rule.prefix] = rule)); + this._onDidRegisterFormatter.fire(); }); } - get onDidRegisterFormatter(): Event { + get onDidRegisterFormatter(): Event { return this._onDidRegisterFormatter.event; } @@ -200,7 +201,7 @@ export class LabelService implements ILabelService { registerFormatter(selector: string, formatter: LabelRules): IDisposable { this.formatters[selector] = formatter; - this._onDidRegisterFormatter.fire({ selector, formatter }); + this._onDidRegisterFormatter.fire(); return { dispose: () => delete this.formatters[selector] From 79818ff2f9441ea091d748f1d439efba89a017c0 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 10 Jan 2019 17:56:59 +0100 Subject: [PATCH 0160/1066] accept drop on root --- .../parts/files/electron-browser/views/explorerViewer.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 4aaeadee93f..300b24fe096 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -453,11 +453,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const items = (data as ElementsDragAndDropData).elements; if (!target) { - if (items[0].isRoot) { - return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; - } - - return false; + return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; } if (!Array.isArray(items)) { From 8c2cef95b3b35acbcfe4e65f9adb1fe81c76a3f8 Mon Sep 17 00:00:00 2001 From: Victorique Ko Date: Fri, 11 Jan 2019 01:04:56 +0800 Subject: [PATCH 0161/1066] Search results for "UTF-8 with BOM" files shifted on first line by a character (#66189) * Fix #66188 * Strip utf-8 bom from matchText * Index can't be negative --- .../search/node/ripgrepTextSearchEngine.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index 81e9d7a417a..f70c901e072 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -237,7 +237,12 @@ export class RipgrepParser extends EventEmitter { private createTextSearchMatch(data: IRgMatch, uri: vscode.Uri): vscode.TextSearchMatch { const lineNumber = data.line_number - 1; - const fullText = bytesOrTextToString(data.lines); + let isBOMStripped = false; + let fullText = bytesOrTextToString(data.lines); + if (lineNumber === 0 && startsWithUTF8BOM(fullText)) { + isBOMStripped = true; + fullText = stripUTF8BOM(fullText); + } const fullTextBytes = Buffer.from(fullText); let prevMatchEnd = 0; @@ -255,6 +260,11 @@ export class RipgrepParser extends EventEmitter { } let matchText = bytesOrTextToString(match.match); + if (lineNumber === 0 && i === 0 && isBOMStripped) { + matchText = stripUTF8BOM(matchText); + match.start = match.start <= 3 ? 0 : match.start - 3; + match.end = match.end <= 3 ? 0 : match.end - 3; + } const inBetweenChars = fullTextBytes.slice(prevMatchEnd, match.start).toString().length; let startCol = prevMatchEndCol + inBetweenChars; @@ -265,12 +275,6 @@ export class RipgrepParser extends EventEmitter { stats.lastLineLength : stats.lastLineLength + startCol; - if (lineNumber === 0 && i === 0 && startsWithUTF8BOM(matchText)) { - matchText = stripUTF8BOM(matchText); - startCol -= 3; - endCol -= 3; - } - prevMatchEnd = match.end; prevMatchEndCol = endCol; prevMatchEndLine = endLineNumber; From fe5f5237351ce301ae41651d90cf3ed489740301 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 9 Jan 2019 11:42:12 -0800 Subject: [PATCH 0162/1066] Add extension information to comment widget model --- src/vs/editor/common/modes.ts | 2 + src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHostComments.ts | 87 +++++++++++-------- .../electron-browser/commentThreadWidget.ts | 10 ++- .../commentsEditorContribution.ts | 19 ++-- 5 files changed, 74 insertions(+), 46 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 6917144a7d1..77c7cdb59ba 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1149,6 +1149,7 @@ export interface Command { * @internal */ export interface CommentInfo { + extensionId: string; threads: CommentThread[]; commentingRanges?: IRange[]; reply?: Command; @@ -1182,6 +1183,7 @@ export enum CommentThreadCollapsibleState { * @internal */ export interface CommentThread { + extensionId: string; threadId: string; resource: string; range: IRange; diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ec3b02beee5..ddf6e61247e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -627,7 +627,7 @@ export function createApiFactory( return extHostSearch.registerFileIndexProvider(scheme, provider); }), registerDocumentCommentProvider: proposedApiFunction(extension, (provider: vscode.DocumentCommentProvider) => { - return exthostCommentProviders.registerDocumentCommentProvider(provider); + return exthostCommentProviders.registerDocumentCommentProvider(extension.identifier, provider); }), registerWorkspaceCommentProvider: proposedApiFunction(extension, (provider: vscode.WorkspaceCommentProvider) => { return exthostCommentProviders.registerWorkspaceCommentProvider(extension.identifier, provider); diff --git a/src/vs/workbench/api/node/extHostComments.ts b/src/vs/workbench/api/node/extHostComments.ts index 6cb4e4c1a59..e814ea68a15 100644 --- a/src/vs/workbench/api/node/extHostComments.ts +++ b/src/vs/workbench/api/node/extHostComments.ts @@ -15,13 +15,19 @@ import { IRange } from 'vs/editor/common/core/range'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +interface HandlerData { + + extensionId: ExtensionIdentifier; + provider: T; +} + export class ExtHostComments implements ExtHostCommentsShape { private static handlePool = 0; private _proxy: MainThreadCommentsShape; - private _documentProviders = new Map(); - private _workspaceProviders = new Map(); + private _documentProviders = new Map>(); + private _workspaceProviders = new Map>(); constructor( mainContext: IMainContext, @@ -36,9 +42,12 @@ export class ExtHostComments implements ExtHostCommentsShape { provider: vscode.WorkspaceCommentProvider ): vscode.Disposable { const handle = ExtHostComments.handlePool++; - this._workspaceProviders.set(handle, provider); + this._workspaceProviders.set(handle, { + extensionId, + provider + }); this._proxy.$registerWorkspaceCommentProvider(handle, extensionId); - this.registerListeners(handle, provider); + this.registerListeners(handle, extensionId, provider); return { dispose: () => { @@ -49,16 +58,20 @@ export class ExtHostComments implements ExtHostCommentsShape { } registerDocumentCommentProvider( + extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider ): vscode.Disposable { const handle = ExtHostComments.handlePool++; - this._documentProviders.set(handle, provider); + this._documentProviders.set(handle, { + extensionId, + provider + }); this._proxy.$registerDocumentCommentProvider(handle, { startDraftLabel: provider.startDraftLabel, deleteDraftLabel: provider.deleteDraftLabel, finishDraftLabel: provider.finishDraftLabel }); - this.registerListeners(handle, provider); + this.registerListeners(handle, extensionId, provider); return { dispose: () => { @@ -76,10 +89,10 @@ export class ExtHostComments implements ExtHostCommentsShape { return Promise.resolve(null); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.createNewCommentThread(data.document, ran, text, CancellationToken.None); - }).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null); + return handlerData.provider.createNewCommentThread(data.document, ran, text, CancellationToken.None); + }).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commandsConverter) : null); } $replyToCommentThread(handle: number, uri: UriComponents, range: IRange, thread: modes.CommentThread, text: string): Promise { @@ -90,10 +103,10 @@ export class ExtHostComments implements ExtHostCommentsShape { return Promise.resolve(null); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None); - }).then(commentThread => commentThread ? convertToCommentThread(provider, commentThread, this._commandsConverter) : null); + return handlerData.provider.replyToCommentThread(data.document, ran, convertFromCommentThread(thread), text, CancellationToken.None); + }).then(commentThread => commentThread ? convertToCommentThread(handlerData.extensionId, handlerData.provider, commentThread, this._commandsConverter) : null); } $editComment(handle: number, uri: UriComponents, comment: modes.Comment, text: string): Promise { @@ -103,9 +116,9 @@ export class ExtHostComments implements ExtHostCommentsShape { throw new Error('Unable to retrieve document from URI'); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.editComment(data.document, convertFromComment(comment), text, CancellationToken.None); + return handlerData.provider.editComment(data.document, convertFromComment(comment), text, CancellationToken.None); }); } @@ -116,9 +129,9 @@ export class ExtHostComments implements ExtHostCommentsShape { throw new Error('Unable to retrieve document from URI'); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None); + return handlerData.provider.deleteComment(data.document, convertFromComment(comment), CancellationToken.None); }); } @@ -129,9 +142,9 @@ export class ExtHostComments implements ExtHostCommentsShape { throw new Error('Unable to retrieve document from URI'); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.startDraft(data.document, CancellationToken.None); + return handlerData.provider.startDraft(data.document, CancellationToken.None); }); } @@ -142,9 +155,9 @@ export class ExtHostComments implements ExtHostCommentsShape { throw new Error('Unable to retrieve document from URI'); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.deleteDraft(data.document, CancellationToken.None); + return handlerData.provider.deleteDraft(data.document, CancellationToken.None); }); } @@ -155,9 +168,9 @@ export class ExtHostComments implements ExtHostCommentsShape { throw new Error('Unable to retrieve document from URI'); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.finishDraft(data.document, CancellationToken.None); + return handlerData.provider.finishDraft(data.document, CancellationToken.None); }); } @@ -167,48 +180,50 @@ export class ExtHostComments implements ExtHostCommentsShape { return Promise.resolve(null); } - const provider = this._documentProviders.get(handle); + const handlerData = this._documentProviders.get(handle); return asPromise(() => { - return provider.provideDocumentComments(data.document, CancellationToken.None); - }).then(commentInfo => commentInfo ? convertCommentInfo(handle, provider, commentInfo, this._commandsConverter) : null); + return handlerData.provider.provideDocumentComments(data.document, CancellationToken.None); + }).then(commentInfo => commentInfo ? convertCommentInfo(handle, handlerData.extensionId, handlerData.provider, commentInfo, this._commandsConverter) : null); } $provideWorkspaceComments(handle: number): Promise { - const provider = this._workspaceProviders.get(handle); - if (!provider) { + const handlerData = this._workspaceProviders.get(handle); + if (!handlerData) { return Promise.resolve(null); } return asPromise(() => { - return provider.provideWorkspaceComments(CancellationToken.None); + return handlerData.provider.provideWorkspaceComments(CancellationToken.None); }).then(comments => - comments.map(comment => convertToCommentThread(provider, comment, this._commandsConverter) + comments.map(comment => convertToCommentThread(handlerData.extensionId, handlerData.provider, comment, this._commandsConverter) )); } - private registerListeners(handle: number, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider) { + private registerListeners(handle: number, extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider) { provider.onDidChangeCommentThreads(event => { this._proxy.$onDidCommentThreadsChange(handle, { - changed: event.changed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)), - added: event.added.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)), - removed: event.removed.map(thread => convertToCommentThread(provider, thread, this._commandsConverter)), + changed: event.changed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)), + added: event.added.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)), + removed: event.removed.map(thread => convertToCommentThread(extensionId, provider, thread, this._commandsConverter)), draftMode: !!(provider as vscode.DocumentCommentProvider).startDraft && !!(provider as vscode.DocumentCommentProvider).finishDraft ? (event.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported }); }); } } -function convertCommentInfo(owner: number, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo { +function convertCommentInfo(owner: number, extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider, vscodeCommentInfo: vscode.CommentInfo, commandsConverter: CommandsConverter): modes.CommentInfo { return { - threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(provider, x, commandsConverter)), + extensionId: extensionId.value, + threads: vscodeCommentInfo.threads.map(x => convertToCommentThread(extensionId, provider, x, commandsConverter)), commentingRanges: vscodeCommentInfo.commentingRanges ? vscodeCommentInfo.commentingRanges.map(range => extHostTypeConverter.Range.from(range)) : [], draftMode: provider.startDraft && provider.finishDraft ? (vscodeCommentInfo.inDraftMode ? modes.DraftMode.InDraft : modes.DraftMode.NotInDraft) : modes.DraftMode.NotSupported }; } -function convertToCommentThread(provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread { +function convertToCommentThread(extensionId: ExtensionIdentifier, provider: vscode.DocumentCommentProvider | vscode.WorkspaceCommentProvider, vscodeCommentThread: vscode.CommentThread, commandsConverter: CommandsConverter): modes.CommentThread { return { + extensionId: extensionId.value, threadId: vscodeCommentThread.threadId, resource: vscodeCommentThread.resource.toString(), range: extHostTypeConverter.Range.from(vscodeCommentThread.range), diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index a6ea9ea01ad..b4462a46bd9 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -80,6 +80,10 @@ export class ReviewZoneWidget extends ZoneWidget { return this._commentThread; } + public get extensionId(): string { + return this._commentThread.extensionId; + } + public get draftMode(): modes.DraftMode { return this._draftMode; } @@ -305,7 +309,11 @@ export class ReviewZoneWidget extends ZoneWidget { this._commentForm = dom.append(this._bodyElement, dom.$('.comment-form')); this._commentEditor = this.instantiationService.createInstance(SimpleCommentEditor, this._commentForm, SimpleCommentEditor.getEditorOptions()); const modeId = hasExistingComments ? this._commentThread.threadId : ++INMEM_MODEL_ID; - const resource = URI.parse(`${COMMENT_SCHEME}:commentinput-${modeId}.md`); + const params = JSON.stringify({ + extensionId: this.extensionId, + commentThreadId: this.commentThread.threadId + }); + const resource = URI.parse(`${COMMENT_SCHEME}:commentinput-${modeId}.md?${params}`); const model = this.modelService.createModel(this._pendingComment || '', this.modeService.createByFilepathOrFirstLine(resource.path), resource, false); this._localToDispose.push(model); this._commentEditor.setModel(model); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index 3de0eb9dc44..c6f1b95096c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -64,7 +64,7 @@ class CommentingRangeDecoration { return this._decorationId; } - constructor(private _editor: ICodeEditor, private _ownerId: string, private _range: IRange, private _reply: modes.Command, commentingOptions: ModelDecorationOptions) { + constructor(private _editor: ICodeEditor, private _ownerId: string, private _extensionId: string, private _range: IRange, private _reply: modes.Command, commentingOptions: ModelDecorationOptions) { const startLineNumber = _range.startLineNumber; const endLineNumber = _range.endLineNumber; let commentingRangeDecorations = [{ @@ -81,8 +81,9 @@ class CommentingRangeDecoration { } } - public getCommentAction(): { replyCommand: modes.Command, ownerId: string } { + public getCommentAction(): { replyCommand: modes.Command, ownerId: string, extensionId: string } { return { + extensionId: this._extensionId, replyCommand: this._reply, ownerId: this._ownerId }; @@ -120,7 +121,7 @@ class CommentingRangeDecorator { let commentingRangeDecorations: CommentingRangeDecoration[] = []; for (const info of commentInfos) { info.commentingRanges.forEach(range => { - commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, range, info.reply, this.decorationOptions)); + commentingRangeDecorations.push(new CommentingRangeDecoration(editor, info.owner, info.extensionId, range, info.reply, this.decorationOptions)); }); } @@ -161,7 +162,7 @@ export class ReviewController implements IEditorContribution { private _computePromise: CancelablePromise | null; private _pendingCommentCache: { [key: number]: { [key: string]: string } }; - private _pendingNewCommentCache: { [key: string]: { lineNumber: number, replyCommand: modes.Command, ownerId: string, pendingComment: string, draftMode: modes.DraftMode } }; + private _pendingNewCommentCache: { [key: string]: { lineNumber: number, replyCommand: modes.Command, ownerId: string, extensionId: string, pendingComment: string, draftMode: modes.DraftMode } }; constructor( editor: ICodeEditor, @@ -325,6 +326,7 @@ export class ReviewController implements IEditorContribution { this._pendingNewCommentCache[e.oldModelUrl.toString()] = { lineNumber: position.lineNumber, ownerId: this._newCommentWidget.owner, + extensionId: this._newCommentWidget.extensionId, replyCommand: this._newCommentWidget.commentThread.reply, pendingComment: pendingNewComment, draftMode: this._newCommentWidget.draftMode @@ -344,7 +346,7 @@ export class ReviewController implements IEditorContribution { if (e.newModelUrl && this._pendingNewCommentCache[e.newModelUrl.toString()]) { let newCommentCache = this._pendingNewCommentCache[e.newModelUrl.toString()]; - this.addComment(newCommentCache.lineNumber, newCommentCache.replyCommand, newCommentCache.ownerId, newCommentCache.draftMode, newCommentCache.pendingComment); + this.addComment(newCommentCache.lineNumber, newCommentCache.replyCommand, newCommentCache.ownerId, newCommentCache.extensionId, newCommentCache.draftMode, newCommentCache.pendingComment); } this.localToDispose.push(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); @@ -402,7 +404,7 @@ export class ReviewController implements IEditorContribution { this.beginCompute(); } - private addComment(lineNumber: number, replyCommand: modes.Command, ownerId: string, draftMode: modes.DraftMode, pendingComment: string) { + private addComment(lineNumber: number, replyCommand: modes.Command, ownerId: string, extensionId: string, draftMode: modes.DraftMode, pendingComment: string) { if (this._newCommentWidget !== null) { this.notificationService.warn(`Please submit the comment at line ${this._newCommentWidget.position.lineNumber} before creating a new one.`); return; @@ -411,6 +413,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, { + extensionId: extensionId, threadId: null, resource: null, comments: [], @@ -502,7 +505,7 @@ export class ReviewController implements IEditorContribution { if (!newCommentInfo) { return; } - const { replyCommand, ownerId } = newCommentInfo; + const { replyCommand, ownerId, extensionId } = newCommentInfo; let commentInfo = this._commentInfos.filter(info => info.owner === ownerId); if (!commentInfo || !commentInfo.length) { @@ -511,7 +514,7 @@ export class ReviewController implements IEditorContribution { let draftMode = commentInfo[0].draftMode; - this.addComment(lineNumber, replyCommand, ownerId, draftMode, null); + this.addComment(lineNumber, replyCommand, ownerId, extensionId, draftMode, null); } } From 3497cecb71531a89a469f4c5b038d9fc19194703 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 10 Jan 2019 10:12:08 -0800 Subject: [PATCH 0163/1066] Clean term env vars in more general way --- .../terminal/node/terminalEnvironment.ts | 28 +++++++------------ .../test/node/terminalEnvironment.test.ts | 3 +- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts index f3acc29ec5a..729951d8eaa 100644 --- a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts @@ -54,25 +54,17 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s export function sanitizeEnvironment(env: ITerminalEnvironment): void { // Remove keys based on strings const keysToRemove = [ - 'ELECTRON_ENABLE_STACK_DUMPING', - 'ELECTRON_ENABLE_LOGGING', - 'ELECTRON_NO_ASAR', - 'ELECTRON_NO_ATTACH_CONSOLE', - 'ELECTRON_RUN_AS_NODE', - 'GOOGLE_API_KEY', - 'VSCODE_CLI', - 'VSCODE_DEV', - 'VSCODE_IPC_HOOK', - 'VSCODE_LOGS', - 'VSCODE_NLS_CONFIG', - 'VSCODE_NODE_CACHED_DATA_DIR', - 'VSCODE_PORTABLE', - 'VSCODE_PID', - 'VSCODE_PREVENT_FOREIGN_INSPECT' + /^ELECTRON_.+$/, + /^GOOGLE_API_KEY$/, + /^VSCODE_.+$/ ]; - keysToRemove.forEach((key) => { - if (env[key]) { - delete env[key]; + const envKeys = Object.keys(env); + envKeys.forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i])) { + delete env[envKey]; + break; + } } }); } 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 9cacf39cad4..44806ac20ba 100644 --- a/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts @@ -48,7 +48,8 @@ suite('Workbench - TerminalEnvironment', () => { VSCODE_NLS_CONFIG: 'x', VSCODE_PORTABLE: 'x', VSCODE_PID: 'x', - VSCODE_NODE_CACHED_DATA_DIR: 'x' + VSCODE_NODE_CACHED_DATA_DIR: 'x', + VSCODE_NEW_VAR: 'x' }; terminalEnvironment.sanitizeEnvironment(env); assert.equal(env['FOO'], 'bar'); From 674807885b97ebe5342ea94e26833277a7339b5a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 10 Jan 2019 19:21:48 +0100 Subject: [PATCH 0164/1066] Implement #42994 --- src/vs/code/node/cliProcessMain.ts | 149 +++++++++++++++-------------- 1 file changed, 77 insertions(+), 72 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 1ec34c0c009..a5b746f9eb3 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -62,8 +62,6 @@ export function getIdAndVersion(id: string): [string, string | undefined] { } -type Task = { (): Promise }; - class Main { constructor( @@ -82,7 +80,7 @@ class Main { } else if (argv['install-extension']) { const arg = argv['install-extension']; const args: string[] = typeof arg === 'string' ? [arg] : arg; - await this.installExtension(args, argv['force']); + await this.installExtensions(args, argv['force']); } else if (argv['uninstall-extension']) { const arg = argv['uninstall-extension']; @@ -100,77 +98,84 @@ class Main { extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } - private installExtension(extensions: string[], force: boolean): Promise { - const vsixTasks: Task[] = extensions - .filter(e => /\.vsix$/i.test(e)) - .map(id => () => { - const extension = path.isAbsolute(id) ? id : path.join(process.cwd(), id); - - return this.validate(extension, force) - .then(valid => { - if (valid) { - return this.extensionManagementService.install(URI.file(extension)).then(() => { - console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension))); - }, error => { - if (isPromiseCanceledError(error)) { - console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", getBaseLabel(extension))); - return null; - } else { - return Promise.reject(error); - } - }); - } - return null; - }); - }); - - const galleryTasks: Task[] = extensions - .filter(e => !/\.vsix$/i.test(e)) - .map(e => () => { - const [id, version] = getIdAndVersion(e); - return this.extensionManagementService.getInstalled(LocalExtensionType.User) - .then(installed => this.extensionGalleryService.getExtension({ id }, version) - .then(null, err => { - if (err.responseText) { - try { - const response = JSON.parse(err.responseText); - return Promise.reject(response.message); - } catch (e) { - // noop - } - } - return Promise.reject(err); - }) - .then(extension => { - if (!extension) { - return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`)); - } - - const [installedExtension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id })); - if (installedExtension) { - if (extension.version !== installedExtension.manifest.version) { - if (version || force) { - console.log(localize('updateMessage', "Updating the Extension '{0}' to the version {1}", id, extension.version)); - return this.installFromGallery(id, extension); - } else { - console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, extension.version)); - return Promise.resolve(null); - } - } else { - console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); - return Promise.resolve(null); - } - } else { - console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id)); - return this.installFromGallery(id, extension); - } - - })); - }); - - return sequence([...vsixTasks, ...galleryTasks]); + private async installExtensions(extensions: string[], force: boolean): Promise { + let failed: string[] = []; + for (const extension of extensions) { + try { + await this.installExtension(extension, force); + } catch (err) { + console.error(err.message || err.stack || err); + failed.push(extension); + } + } + return failed.length ? Promise.reject(localize('installation failed', "Failed Installing Extensions: {0}", failed.join(', '))) : Promise.resolve(); } + private installExtension(extension: string, force: boolean): Promise { + if (/\.vsix$/i.test(extension)) { + extension = path.isAbsolute(extension) ? extension : path.join(process.cwd(), extension); + + return this.validate(extension, force) + .then(valid => { + if (valid) { + return this.extensionManagementService.install(URI.file(extension)).then(() => { + console.log(localize('successVsixInstall', "Extension '{0}' was successfully installed!", getBaseLabel(extension))); + }, error => { + if (isPromiseCanceledError(error)) { + console.log(localize('cancelVsixInstall', "Cancelled installing Extension '{0}'.", getBaseLabel(extension))); + return null; + } else { + return Promise.reject(error); + } + }); + } + return null; + }); + } + + const [id, version] = getIdAndVersion(extension); + return this.extensionManagementService.getInstalled(LocalExtensionType.User) + .then(installed => this.extensionGalleryService.getExtension({ id }, version) + .then(null, err => { + if (err.responseText) { + try { + const response = JSON.parse(err.responseText); + return Promise.reject(response.message); + } catch (e) { + // noop + } + } + return Promise.reject(err); + }) + .then(extension => { + if (!extension) { + return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`)); + } + + const [installedExtension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id })); + if (installedExtension) { + if (extension.version !== installedExtension.manifest.version) { + if (version || force) { + console.log(localize('updateMessage', "Updating the Extension '{0}' to the version {1}", id, extension.version)); + return this.installFromGallery(id, extension); + } else { + console.log(localize('forceUpdate', "Extension '{0}' v{1} is already installed, but a newer version {2} is available in the marketplace. Use '--force' option to update to newer version.", id, installedExtension.manifest.version, extension.version)); + return Promise.resolve(null); + } + } else { + console.log(localize('alreadyInstalled', "Extension '{0}' is already installed.", version ? `${id}@${version}` : id)); + return Promise.resolve(null); + } + } else { + console.log(localize('foundExtension', "Found '{0}' in the marketplace.", id)); + return this.installFromGallery(id, extension); + } + + })); + } + + + private async validate(vsix: string, force: boolean): Promise { const manifest = await getManifest(vsix); From 8b2726d531c784762750d37a584706da1a309e83 Mon Sep 17 00:00:00 2001 From: Tyler Leonhardt Date: Thu, 10 Jan 2019 10:23:19 -0800 Subject: [PATCH 0165/1066] Handle he edge case where there's a single quote but no space --- .../parts/terminal/electron-browser/terminalInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 3db710c9cda..598227eb4fc 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -801,7 +801,7 @@ export class TerminalInstance implements ITerminalInstance { pathBasename === 'powershell' || this.title === 'powershell'; - if (hasSpace && isPowerShell) { + if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { c(`& '${originalPath.replace('\'', '\'\'')}'`); return; } From 14aa41aca780a28fdd992b9b66fce6e9de2207ab Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 10 Jan 2019 11:11:31 -0800 Subject: [PATCH 0166/1066] Use fsPath over path for terminal cwd --- .../parts/terminal/electron-browser/terminalProcessManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index 50e007da70c..a4fdb4ed501 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -95,7 +95,7 @@ export class TerminalProcessManager implements ITerminalProcessManager { if (shellLaunchConfig.cwd && typeof shellLaunchConfig.cwd === 'object') { launchRemotely = !!getRemoteAuthority(shellLaunchConfig.cwd); - shellLaunchConfig.cwd = shellLaunchConfig.cwd.path; + shellLaunchConfig.cwd = shellLaunchConfig.cwd.fsPath; } else { launchRemotely = !!this._windowService.getConfiguration().remoteAuthority; } From 31d776bb513fe9a16c85b45322584df82378c3bb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 11:52:30 -0800 Subject: [PATCH 0167/1066] Cancel code action requests when the code action registry changes Fixes #66322 --- .../contrib/codeAction/codeActionModel.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index 5aa433d5a45..aa08a4c20db 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -172,6 +172,7 @@ export class CodeActionModel { private _editor: ICodeEditor; private _markerService: IMarkerService; private _codeActionOracle?: CodeActionOracle; + private _state: CodeActionsState.State = CodeActionsState.Empty; private _onDidChangeState = new Emitter(); private _disposables: IDisposable[] = []; private readonly _supportedCodeActions: IContextKey; @@ -184,7 +185,7 @@ export class CodeActionModel { this._disposables.push(this._editor.onDidChangeModel(() => this._update())); this._disposables.push(this._editor.onDidChangeModelLanguage(() => this._update())); - this._disposables.push(CodeActionProviderRegistry.onDidChange(this._update, this)); + this._disposables.push(CodeActionProviderRegistry.onDidChange(() => this._update())); this._update(); } @@ -199,19 +200,21 @@ export class CodeActionModel { } private _update(): void { - if (this._codeActionOracle) { this._codeActionOracle.dispose(); this._codeActionOracle = undefined; - this._onDidChangeState.fire(CodeActionsState.Empty); } + if (this._state.type === CodeActionsState.Type.Triggered) { + this._state.actions.cancel(); + } + this.setState(CodeActionsState.Empty); + const model = this._editor.getModel(); if (model && CodeActionProviderRegistry.has(model) && !this._editor.getConfiguration().readOnly ) { - const supportedActions: string[] = []; for (const provider of CodeActionProviderRegistry.all(model)) { if (Array.isArray(provider.providedCodeActionKinds)) { @@ -221,17 +224,25 @@ export class CodeActionModel { this._supportedCodeActions.set(supportedActions.join(' ')); - this._codeActionOracle = new CodeActionOracle(this._editor, this._markerService, newState => this._onDidChangeState.fire(newState), undefined, this._progressService); + this._codeActionOracle = new CodeActionOracle(this._editor, this._markerService, newState => this.setState(newState), undefined, this._progressService); this._codeActionOracle.trigger({ type: 'auto' }); } else { this._supportedCodeActions.reset(); } } - trigger(trigger: CodeActionTrigger): Promise { + public trigger(trigger: CodeActionTrigger): Promise { if (this._codeActionOracle) { return this._codeActionOracle.trigger(trigger); } return Promise.resolve(undefined); } + + private setState(newState: CodeActionsState.State) { + if (newState === this._state) { + return; + } + this._state = newState; + this._onDidChangeState.fire(newState); + } } From a62b481bdbf8d512aeba7ff6badceff02f5229a0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 11:57:04 -0800 Subject: [PATCH 0168/1066] Some strict null checks in progressService --- .../services/progress/browser/progressService2.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/services/progress/browser/progressService2.ts b/src/vs/workbench/services/progress/browser/progressService2.ts index 5b10a77f060..606793db659 100644 --- a/src/vs/workbench/services/progress/browser/progressService2.ts +++ b/src/vs/workbench/services/progress/browser/progressService2.ts @@ -65,7 +65,7 @@ export class ProgressService2 implements IProgressService2 { const promise = callback(task[1]); - let delayHandle = setTimeout(() => { + let delayHandle: any = setTimeout(() => { delayHandle = undefined; this._stack.unshift(task); this._updateWindowProgress(); @@ -131,7 +131,7 @@ export class ProgressService2 implements IProgressService2 { private _withNotificationProgress

, R=any>(options: IProgressOptions, callback: (progress: IProgress<{ message?: string, increment?: number }>) => P, onDidCancel?: () => void): P { const toDispose: IDisposable[] = []; - const createNotification = (message: string, increment?: number): INotificationHandle => { + const createNotification = (message: string | undefined, increment?: number): INotificationHandle | undefined => { if (!message) { return undefined; // we need a message at least } @@ -140,7 +140,7 @@ export class ProgressService2 implements IProgressService2 { if (options.cancellable) { const cancelAction = new class extends Action { constructor() { - super('progress.cancel', localize('cancel', "Cancel"), null, true); + super('progress.cancel', localize('cancel', "Cancel"), undefined, true); } run(): Promise { @@ -153,7 +153,7 @@ export class ProgressService2 implements IProgressService2 { }; toDispose.push(cancelAction); - actions.primary.push(cancelAction); + actions.primary!.push(cancelAction); } const handle = this._notificationService.notify({ @@ -181,7 +181,7 @@ export class ProgressService2 implements IProgressService2 { } }; - let handle: INotificationHandle; + let handle: INotificationHandle | undefined; const updateNotification = (message?: string, increment?: number): void => { if (!handle) { handle = createNotification(message, increment); @@ -235,7 +235,7 @@ export class ProgressService2 implements IProgressService2 { // show activity bar let activityProgress: IDisposable; - let delayHandle = setTimeout(() => { + let delayHandle: any = setTimeout(() => { delayHandle = undefined; const handle = this._activityBar.showActivity( viewletId, From c90f92f3fc3f138edd93336c3bb69fd9ce7cbf2c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 12:00:39 -0800 Subject: [PATCH 0169/1066] Strict null check tabCompletions --- src/tsconfig.strictNullChecks.json | 2 ++ .../parts/snippets/electron-browser/tabCompletion.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index d8e35e83f6b..09d49339198 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -671,6 +671,7 @@ "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", "./vs/workbench/parts/snippets/electron-browser/snippetsFile.ts", "./vs/workbench/parts/snippets/electron-browser/snippetsService.ts", + "./vs/workbench/parts/snippets/electron-browser/tabCompletion.ts", "./vs/workbench/parts/snippets/test/electron-browser/snippetFile.test.ts", "./vs/workbench/parts/snippets/test/electron-browser/snippetsRegistry.test.ts", "./vs/workbench/parts/snippets/test/electron-browser/snippetsRewrite.test.ts", @@ -812,6 +813,7 @@ "./vs/workbench/services/keybinding/test/macLinuxFallbackKeyboardMapper.test.ts", "./vs/workbench/services/keybinding/test/macLinuxKeyboardMapper.test.ts", "./vs/workbench/services/keybinding/test/windowsKeyboardMapper.test.ts", + "./vs/workbench/services/label/common/labelService.ts", "./vs/workbench/services/mode/common/workbenchModeService.ts", "./vs/workbench/services/notification/common/notificationService.ts", "./vs/workbench/services/panel/common/panelService.ts", diff --git a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts index c42b25bba61..41affd11eb8 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/tabCompletion.ts @@ -78,6 +78,10 @@ export class TabCompletionController implements editorCommon.IEditorContribution // reset first this._activeSnippets = []; + if (!this._editor.hasModel()) { + return; + } + // lots of dance for getting the const selection = this._editor.getSelection(); const model = this._editor.getModel(); @@ -118,6 +122,9 @@ export class TabCompletionController implements editorCommon.IEditorContribution } performSnippetCompletions(): void { + if (!this._editor.hasModel()) { + return; + } if (this._activeSnippets.length === 1) { // one -> just insert @@ -126,8 +133,8 @@ export class TabCompletionController implements editorCommon.IEditorContribution } else if (this._activeSnippets.length > 1) { // two or more -> show IntelliSense box + const position = this._editor.getPosition(); showSimpleSuggestions(this._editor, this._activeSnippets.map(snippet => { - const position = this._editor.getPosition(); const range = Range.fromPositions(position.delta(0, -snippet.prefix.length), position); return new SnippetCompletion(snippet, range); })); From c9a90bc626526ac35e7ca7ff86b2deedbd373cb2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 14:24:41 -0800 Subject: [PATCH 0170/1066] Parameter hints should restore editor focus after using buttons Fixes #65435 --- src/vs/editor/contrib/parameterHints/parameterHints.ts | 6 +++--- .../editor/contrib/parameterHints/parameterHintsWidget.ts | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.ts b/src/vs/editor/contrib/parameterHints/parameterHints.ts index 2a6ac351cd9..eb846502b63 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHints.ts @@ -98,7 +98,7 @@ registerEditorCommand(new ParameterHintsCommand({ handler: x => x.cancel(), kbOpts: { weight: weight, - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.focus, primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] } @@ -109,7 +109,7 @@ registerEditorCommand(new ParameterHintsCommand({ handler: x => x.previous(), kbOpts: { weight: weight, - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.focus, primary: KeyCode.UpArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow], mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.Alt | KeyCode.UpArrow, KeyMod.WinCtrl | KeyCode.KEY_P] } @@ -121,7 +121,7 @@ registerEditorCommand(new ParameterHintsCommand({ handler: x => x.next(), kbOpts: { weight: weight, - kbExpr: EditorContextKeys.editorTextFocus, + kbExpr: EditorContextKeys.focus, primary: KeyCode.DownArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow], mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.Alt | KeyCode.DownArrow, KeyMod.WinCtrl | KeyCode.KEY_N] } diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index 77918a35819..b953d356024 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -284,12 +284,14 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable { next(): void { if (this.model) { + this.editor.focus(); this.model.next(); } } previous(): void { if (this.model) { + this.editor.focus(); this.model.previous(); } } From ccdd2997a66965c7104fe180207d9a710ea50fa5 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 10 Jan 2019 17:43:25 -0800 Subject: [PATCH 0171/1066] update AI module (#66370) fixes #64849 --- extensions/git/package.json | 2 +- extensions/git/yarn.lock | 18 +++++++++--------- extensions/html-language-features/package.json | 2 +- extensions/html-language-features/yarn.lock | 18 +++++++++--------- extensions/json-language-features/package.json | 2 +- extensions/json-language-features/yarn.lock | 18 +++++++++--------- .../markdown-language-features/package.json | 2 +- .../markdown-language-features/yarn.lock | 18 +++++++++--------- .../typescript-language-features/package.json | 2 +- .../typescript-language-features/yarn.lock | 18 +++++++++--------- package.json | 2 +- yarn.lock | 8 ++++---- 12 files changed, 55 insertions(+), 55 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index e334c0a5251..fdc08df06a8 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1417,7 +1417,7 @@ "file-type": "^7.2.0", "iconv-lite": "^0.4.24", "jschardet": "^1.6.0", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0", "which": "^1.3.0" }, diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 616d42caddf..1d859c5e73b 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -36,10 +36,10 @@ resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -313,12 +313,12 @@ supports-color@3.1.2: dependencies: has-flag "^1.0.0" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 78236179394..01299004ebb 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -184,7 +184,7 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "^5.1.0", "vscode-nls": "^4.0.0" }, diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 236dc76b3f5..ee9e450ed7c 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -33,12 +33,12 @@ semver@^5.3.0, semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-jsonrpc@^4.0.0: version "4.0.0" diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index a1a5204c8b5..1eb1e6e2f88 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -100,7 +100,7 @@ } }, "dependencies": { - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-languageclient": "^5.1.0", "vscode-nls": "^4.0.0" }, diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index cfcbd4f4cbe..019393942b3 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.25.tgz#801fe4e39372cef18f268db880a5fbfcf71adc7e" integrity sha512-WXvAXaknB0c2cJ7N44e1kUrVu5K90mSfPPaT5XxfuSMxEWva86EYIwxUZM3jNZ2P1CIC9e2z4WJqpAF69PQxeA== -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -38,12 +38,12 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-jsonrpc@^4.0.0: version "4.0.0" diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 2e13c9aaa38..d5da4b5aef6 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -298,7 +298,7 @@ "highlight.js": "9.13.1", "markdown-it": "^8.4.2", "markdown-it-front-matter": "^0.1.2", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index af3977a75cc..e63c5f4da91 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -159,10 +159,10 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -6313,12 +6313,12 @@ vm-browserify@0.0.4: dependencies: indexof "0.0.1" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 0cea8d8ec7c..63fcf31bad9 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -18,7 +18,7 @@ "dependencies": { "jsonc-parser": "^2.0.1", "semver": "5.5.1", - "vscode-extension-telemetry": "0.1.0", + "vscode-extension-telemetry": "0.1.1", "vscode-nls": "^4.0.0" }, "devDependencies": { diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index ff4f999787c..62a9ee588d1 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -58,10 +58,10 @@ ansi-wrap@0.1.0: resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= -applicationinsights@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.6.tgz#bc201810de91cea910dab34e8ad35ecde488edeb" - integrity sha512-VQT3kBpJVPw5fCO5n+WUeSx0VHjxFtD7znYbILBlVgOS9/cMDuGFmV2Br3ObzFyZUDGNbEfW36fD1y2/vAiCKw== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" @@ -1810,12 +1810,12 @@ vinyl@^2.0.1, vinyl@^2.0.2: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-extension-telemetry@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.0.tgz#3cdcb61d03829966bd04b5f11471a1e40d6abaad" - integrity sha512-WVCnP+uLxlqB6UD98yQNV47mR5Rf79LFxpuZhSPhEf0Sb4tPZed3a63n003/dchhOwyCTCBuNN4n8XKJkLEI1Q== +vscode-extension-telemetry@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.1.tgz#91387e06b33400c57abd48979b0e790415ae110b" + integrity sha512-TkKKG/B/J94DP5qf6xWB4YaqlhWDg6zbbqVx7Bz//stLQNnfE9XS1xm3f6fl24c5+bnEK0/wHgMgZYKIKxPeUA== dependencies: - applicationinsights "1.0.6" + applicationinsights "1.0.8" vscode-nls@^4.0.0: version "4.0.0" diff --git a/package.json b/package.json index 884f7e1c82b..ca7ec411751 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "strict-null-check-watch": "tsc -p src/tsconfig.strictNullChecks.json --watch" }, "dependencies": { - "applicationinsights": "1.0.7", + "applicationinsights": "1.0.8", "fast-plist": "0.1.2", "gc-signals": "^0.0.1", "getmac": "1.4.1", diff --git a/yarn.lock b/yarn.lock index 8bd74330231..a02896fb7f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -460,10 +460,10 @@ append-buffer@^1.0.2: dependencies: buffer-equal "^1.0.0" -applicationinsights@1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.7.tgz#f8d5758b99e5c3e5b2877cfa120c2164a5cf675b" - integrity sha512-h2v03PzxrsFe/kiPt1FTRKUjVXBln2ZqCCGByeeBbFt8nkyWHfLV6pBgRRRJjMQ0IZCnayzH0lPm4XeOCqugoQ== +applicationinsights@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" + integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== dependencies: diagnostic-channel "0.2.0" diagnostic-channel-publishers "0.2.1" From e314bce7db5370544b7a61757414d03b7bb86366 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 11 Jan 2019 09:49:23 +0100 Subject: [PATCH 0172/1066] fix --prof-modules flags --- src/vs/code/electron-browser/workbench/workbench.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index decf98fc7ee..84c258ca734 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -35,7 +35,7 @@ bootstrapWindow.load([ showPartsSplash(windowConfig); }, beforeLoaderConfig: function (windowConfig, loaderConfig) { - loaderConfig.recordStats = !!windowConfig.performance; + loaderConfig.recordStats = !!windowConfig['prof-modules']; if (loaderConfig.nodeCachedData) { const onNodeCachedData = window['MonacoEnvironment'].onNodeCachedData = []; loaderConfig.nodeCachedData.onData = function () { From 85154cfdc3c2121bb995a83a89acc2dbbbd4f4a0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 11 Jan 2019 10:26:07 +0100 Subject: [PATCH 0173/1066] [loc] The phrase 'The color must not be opaque to not hide underlying decorations' is incorrect. Fixes #66333 --- .../editor/common/view/editorColorRegistry.ts | 4 +-- .../wordHighlighter/wordHighlighter.ts | 8 ++--- src/vs/platform/theme/common/colorRegistry.ts | 32 +++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 6d3d197dc61..27ccaf58e6b 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -13,7 +13,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic */ export const editorLineHighlight = registerColor('editor.lineHighlightBackground', { dark: null, light: null, hc: null }, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hc: '#f38518' }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); -export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque to not hide underlying decorations.'), true); +export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hc: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hc: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.'), true); export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hc: Color.white }, nls.localize('caret', 'Color of the editor cursor.')); @@ -53,7 +53,7 @@ export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode. export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); -export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: new Color(new RGBA(18, 136, 18, 0.7)), light: new Color(new RGBA(18, 136, 18, 0.7)), hc: new Color(new RGBA(50, 255, 50, 1)) }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: new Color(new RGBA(18, 18, 136, 0.7)), light: new Color(new RGBA(18, 18, 136, 0.7)), hc: new Color(new RGBA(50, 50, 255, 1)) }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 9a7accdafbc..036f26483fb 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -27,13 +27,13 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { activeContrastBorder, editorSelectionHighlight, editorSelectionHighlightBorder, overviewRulerSelectionHighlightForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; -export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque to not hide underlying decorations.'), true); -export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque to not hide underlying decorations.'), true); +export const editorWordHighlight = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hc: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations.'), true); +export const editorWordHighlightStrong = registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hc: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWordHighlightBorder = registerColor('editor.wordHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('wordHighlightBorder', 'Border color of a symbol during read-access, like reading a variable.')); export const editorWordHighlightStrongBorder = registerColor('editor.wordHighlightStrongBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('wordHighlightStrongBorder', 'Border color of a symbol during write-access, like writing to a variable.')); -export const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque to not hide underlying decorations.'), true); -export const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0CC', light: '#C0A0C0CC', hc: '#C0A0C0CC' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +export const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0CC', light: '#C0A0C0CC', hc: '#C0A0C0CC' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 8d47f59a101..d239891f730 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -258,8 +258,8 @@ export const editorWidgetResizeBorder = registerColor('editorWidget.resizeBorder */ export const editorSelectionBackground = registerColor('editor.selectionBackground', { light: '#ADD6FF', dark: '#264F78', hc: '#f3f518' }, nls.localize('editorSelectionBackground', "Color of the editor selection.")); export const editorSelectionForeground = registerColor('editor.selectionForeground', { light: null, dark: null, hc: '#000000' }, nls.localize('editorSelectionForeground', "Color of the selected text for high contrast.")); -export const editorInactiveSelection = registerColor('editor.inactiveSelectionBackground', { light: transparent(editorSelectionBackground, 0.5), dark: transparent(editorSelectionBackground, 0.5), hc: transparent(editorSelectionBackground, 0.5) }, nls.localize('editorInactiveSelection', "Color of the selection in an inactive editor. The color must not be opaque to not hide underlying decorations."), true); -export const editorSelectionHighlight = registerColor('editor.selectionHighlightBackground', { light: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), dark: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), hc: null }, nls.localize('editorSelectionHighlight', 'Color for regions with the same content as the selection. The color must not be opaque to not hide underlying decorations.'), true); +export const editorInactiveSelection = registerColor('editor.inactiveSelectionBackground', { light: transparent(editorSelectionBackground, 0.5), dark: transparent(editorSelectionBackground, 0.5), hc: transparent(editorSelectionBackground, 0.5) }, nls.localize('editorInactiveSelection', "Color of the selection in an inactive editor. The color must not be opaque so as not to hide underlying decorations."), true); +export const editorSelectionHighlight = registerColor('editor.selectionHighlightBackground', { light: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), dark: lessProminent(editorSelectionBackground, editorBackground, 0.3, 0.6), hc: null }, nls.localize('editorSelectionHighlight', 'Color for regions with the same content as the selection. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorSelectionHighlightBorder = registerColor('editor.selectionHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('editorSelectionHighlightBorder', "Border color for regions with the same content as the selection.")); @@ -267,16 +267,16 @@ export const editorSelectionHighlightBorder = registerColor('editor.selectionHig * Editor find match colors. */ export const editorFindMatch = registerColor('editor.findMatchBackground', { light: '#A8AC94', dark: '#515C6A', hc: null }, nls.localize('editorFindMatch', "Color of the current search match.")); -export const editorFindMatchHighlight = registerColor('editor.findMatchHighlightBackground', { light: '#EA5C0055', dark: '#EA5C0055', hc: null }, nls.localize('findMatchHighlight', "Color of the other search matches. The color must not be opaque to not hide underlying decorations."), true); -export const editorFindRangeHighlight = registerColor('editor.findRangeHighlightBackground', { dark: '#3a3d4166', light: '#b4b4b44d', hc: null }, nls.localize('findRangeHighlight', "Color of the range limiting the search. The color must not be opaque to not hide underlying decorations."), true); +export const editorFindMatchHighlight = registerColor('editor.findMatchHighlightBackground', { light: '#EA5C0055', dark: '#EA5C0055', hc: null }, nls.localize('findMatchHighlight', "Color of the other search matches. The color must not be opaque so as not to hide underlying decorations."), true); +export const editorFindRangeHighlight = registerColor('editor.findRangeHighlightBackground', { dark: '#3a3d4166', light: '#b4b4b44d', hc: null }, nls.localize('findRangeHighlight', "Color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); export const editorFindMatchBorder = registerColor('editor.findMatchBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('editorFindMatchBorder', "Border color of the current search match.")); export const editorFindMatchHighlightBorder = registerColor('editor.findMatchHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('findMatchHighlightBorder', "Border color of the other search matches.")); -export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque to not hide underlying decorations."), true); +export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); /** * Editor hover */ -export const editorHoverHighlight = registerColor('editor.hoverHighlightBackground', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown. The color must not be opaque to not hide underlying decorations.'), true); +export const editorHoverHighlight = registerColor('editor.hoverHighlightBackground', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorHoverBackground = registerColor('editorHoverWidget.background', { light: editorWidgetBackground, dark: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('hoverBackground', 'Background color of the editor hover.')); export const editorHoverBorder = registerColor('editorHoverWidget.border', { light: editorWidgetBorder, dark: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('hoverBorder', 'Border color of the editor hover.')); @@ -291,8 +291,8 @@ export const editorActiveLinkForeground = registerColor('editorLink.activeForegr export const defaultInsertColor = new Color(new RGBA(155, 185, 85, 0.2)); export const defaultRemoveColor = new Color(new RGBA(255, 0, 0, 0.2)); -export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque to not hide underlying decorations.'), true); -export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque to not hide underlying decorations.'), true); +export const diffInserted = registerColor('diffEditor.insertedTextBackground', { dark: defaultInsertColor, light: defaultInsertColor, hc: null }, nls.localize('diffEditorInserted', 'Background color for text that got inserted. The color must not be opaque so as not to hide underlying decorations.'), true); +export const diffRemoved = registerColor('diffEditor.removedTextBackground', { dark: defaultRemoveColor, light: defaultRemoveColor, hc: null }, nls.localize('diffEditorRemoved', 'Background color for text that got removed. The color must not be opaque so as not to hide underlying decorations.'), true); export const diffInsertedOutline = registerColor('diffEditor.insertedTextBorder', { dark: null, light: null, hc: '#33ff2eff' }, nls.localize('diffEditorInsertedOutline', 'Outline color for the text that got inserted.')); export const diffRemovedOutline = registerColor('diffEditor.removedTextBorder', { dark: null, light: null, hc: '#FF008F' }, nls.localize('diffEditorRemovedOutline', 'Outline color for text that got removed.')); @@ -327,12 +327,12 @@ const commonBaseColor = Color.fromHex('#606060').transparent(0.4); const contentTransparency = 0.4; const rulerTransparency = 1; -export const mergeCurrentHeaderBackground = registerColor('merge.currentHeaderBackground', { dark: currentBaseColor, light: currentBaseColor, hc: null }, nls.localize('mergeCurrentHeaderBackground', 'Current header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCurrentContentBackground = registerColor('merge.currentContentBackground', { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hc: transparent(mergeCurrentHeaderBackground, contentTransparency) }, nls.localize('mergeCurrentContentBackground', 'Current content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeaderBackground', { dark: incomingBaseColor, light: incomingBaseColor, hc: null }, nls.localize('mergeIncomingHeaderBackground', 'Incoming header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeIncomingContentBackground = registerColor('merge.incomingContentBackground', { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hc: transparent(mergeIncomingHeaderBackground, contentTransparency) }, nls.localize('mergeIncomingContentBackground', 'Incoming content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBackground', { dark: commonBaseColor, light: commonBaseColor, hc: null }, nls.localize('mergeCommonHeaderBackground', 'Common ancestor header background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); -export const mergeCommonContentBackground = registerColor('merge.commonContentBackground', { dark: transparent(mergeCommonHeaderBackground, contentTransparency), light: transparent(mergeCommonHeaderBackground, contentTransparency), hc: transparent(mergeCommonHeaderBackground, contentTransparency) }, nls.localize('mergeCommonContentBackground', 'Common ancestor content background in inline merge-conflicts. The color must not be opaque to not hide underlying decorations.'), true); +export const mergeCurrentHeaderBackground = registerColor('merge.currentHeaderBackground', { dark: currentBaseColor, light: currentBaseColor, hc: null }, nls.localize('mergeCurrentHeaderBackground', 'Current header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCurrentContentBackground = registerColor('merge.currentContentBackground', { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hc: transparent(mergeCurrentHeaderBackground, contentTransparency) }, nls.localize('mergeCurrentContentBackground', 'Current content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeaderBackground', { dark: incomingBaseColor, light: incomingBaseColor, hc: null }, nls.localize('mergeIncomingHeaderBackground', 'Incoming header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeIncomingContentBackground = registerColor('merge.incomingContentBackground', { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hc: transparent(mergeIncomingHeaderBackground, contentTransparency) }, nls.localize('mergeIncomingContentBackground', 'Incoming content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBackground', { dark: commonBaseColor, light: commonBaseColor, hc: null }, nls.localize('mergeCommonHeaderBackground', 'Common ancestor header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); +export const mergeCommonContentBackground = registerColor('merge.commonContentBackground', { dark: transparent(mergeCommonHeaderBackground, contentTransparency), light: transparent(mergeCommonHeaderBackground, contentTransparency), hc: transparent(mergeCommonHeaderBackground, contentTransparency) }, nls.localize('mergeCommonContentBackground', 'Common ancestor content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeBorder = registerColor('merge.border', { dark: null, light: null, hc: '#C3DF6F' }, nls.localize('mergeBorder', 'Border color on headers and the splitter in inline merge-conflicts.')); @@ -341,9 +341,9 @@ export const overviewRulerIncomingContentForeground = registerColor('editorOverv export const overviewRulerCommonContentForeground = registerColor('editorOverviewRuler.commonContentForeground', { dark: transparent(mergeCommonHeaderBackground, rulerTransparency), light: transparent(mergeCommonHeaderBackground, rulerTransparency), hc: mergeBorder }, nls.localize('overviewRulerCommonContentForeground', 'Common ancestor overview ruler foreground for inline merge-conflicts.')); const findMatchColorDefault = new Color(new RGBA(246, 185, 77, 0.7)); -export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', { dark: findMatchColorDefault, light: findMatchColorDefault, hc: findMatchColorDefault }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', { dark: findMatchColorDefault, light: findMatchColorDefault, hc: findMatchColorDefault }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.'), true); -export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque to not hide underlying decorations.'), true); +export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hc: '#A0A0A0CC' }, nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.'), true); // ----- color functions From a8520a86dc451542e422e6cabc6d9bba1dddd1a0 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 11:13:18 +0100 Subject: [PATCH 0174/1066] explorer dnd: properly compute drop target --- .../electron-browser/views/explorerViewer.ts | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) 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 300b24fe096..b06374ab048 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -541,11 +541,21 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } drop(data: IDragAndDropData, target: ExplorerItem, targetIndex: number, originalEvent: DragEvent): void { + // Find parent to add to + if (!target) { + target = this.explorerService.roots[this.explorerService.roots.length - 1]; + } + if (!target.isDirectory) { + target = target.parent; + } + if (target.isReadonly) { + return; + } + // Desktop DND (Import file) if (data instanceof DesktopDragAndDropData) { this.handleExternalDrop(data, target, originalEvent); } - // In-Explorer DND (Move/Copy file) else { this.handleExplorerDrop(data, target, originalEvent); @@ -586,7 +596,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } // Handle dropped files (only support FileStat as target) - else if (target instanceof ExplorerItem && !target.isReadonly) { + else if (target instanceof ExplorerItem) { return this.addResources(target, droppedResources.map(res => res.resource)); } @@ -597,15 +607,6 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private addResources(target: ExplorerItem, resources: URI[]): Promise { if (resources && resources.length > 0) { - // Find parent to add to - if (!target) { - target = this.explorerService.roots[0]; - } - - if (!target.isDirectory) { - target = target.parent; - } - // Resolve target to check for name collisions and ask user return this.fileService.resolveFile(target.resource).then((targetStat: IFileStat) => { @@ -670,7 +671,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return Promise.resolve(undefined); } - private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem | undefined, originalEvent: DragEvent): Promise { + private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise { const elementsData = (data as ElementsDragAndDropData).elements; const items = distinctParents(elementsData, s => s.resource); const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh); @@ -714,7 +715,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem | undefined): Promise { + private doHandleRootDrop(roots: ExplorerItem[], target: ExplorerItem): Promise { if (roots.length === 0) { return Promise.resolve(undefined); } @@ -738,24 +739,12 @@ export class FileDragAndDrop implements ITreeDragAndDrop { rootsToMove.push(data); } } - if (!target) { - // Empty area - targetIndex = workspaceCreationData.length; - } workspaceCreationData.splice(targetIndex, 0, ...rootsToMove); return this.workspaceEditingService.updateFolders(0, workspaceCreationData.length, workspaceCreationData); } - private doHandleExplorerDrop(source: ExplorerItem, target: ExplorerItem | undefined, isCopy: boolean): Promise { - if (!(target instanceof ExplorerItem)) { - return Promise.resolve(undefined); - } - - if (target.isReadonly) { - return undefined; - } - + private doHandleExplorerDrop(source: ExplorerItem, target: ExplorerItem, isCopy: boolean): Promise { // Reuse duplicate action if user copies if (isCopy) { From 69e75ec17f74d10b9236d1534c403a46c2497bf3 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 11:47:26 +0100 Subject: [PATCH 0175/1066] explorer: do not always recursvily refresh --- .../parts/files/electron-browser/views/explorerView.ts | 4 +++- 1 file changed, 3 insertions(+), 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 e32a1a11cd2..c538216e9fd 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -350,15 +350,17 @@ export class ExplorerView extends ViewletPanel { /** * Refresh the contents of the explorer to get up to date data from the disk about the file structure. + * If the item is passed we refresh only that level of the tree, otherwise we do a full refresh. */ refresh(item?: ExplorerItem): Promise { if (!this.tree || !this.isBodyVisible()) { this.shouldRefresh = true; return Promise.resolve(undefined); } + const recursive = !item; const toRefresh = item || this.tree.getInput(); - return this.tree.refresh(toRefresh, true); + return this.tree.refresh(toRefresh, recursive); } getOptimalWidth(): number { From a55fd35137c246f8ba756e871ace362db5d57998 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 11 Jan 2019 12:10:33 +0100 Subject: [PATCH 0176/1066] storage - always recover from backup in case of an error opening on startup (#66396) --- src/vs/base/node/storage.ts | 81 ++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 14936051232..bd8b9b0bc6a 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -11,7 +11,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; import { mark } from 'vs/base/common/performance'; -import { rename, copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; +import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; export enum StorageHint { @@ -514,48 +514,41 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } - private connect(path: string): Promise { - this.logger.trace(`[storage ${this.name}] open()`); + private connect(path: string, retryOnBusy: boolean = true): Promise { + this.logger.trace(`[storage ${this.name}] open(${path}, retryOnBusy: ${retryOnBusy})`); - return new Promise((resolve, reject) => { - const fallbackToInMemoryDatabase = (error: Error) => { - this.logger.error(`[storage ${this.name}] open(): Error (open DB): ${error}. Falling back to in-memory DB`); + return this.doConnect(path).then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error}`); - // In case of any error to open the DB, use an in-memory - // DB so that we always have a valid DB to talk to. - this.doConnect(SQLiteStorageDatabase.IN_MEMORY_PATH).then(resolve, reject); - }; + // SQLITE_BUSY should only arise if another process is locking the same DB we want + // to open at that time. This typically never happens because a DB connection is + // limited per window. However, in the event of a window reload, it may be possible + // that the previous connection was not properly closed while the new connection is + // already established. + // + // In this case we simply wait for some time and retry once to establish the connection. + // + if (error.code === 'SQLITE_BUSY' && retryOnBusy) { + return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.connect(path, false /* not another retry */)); + } - return this.doConnect(path).then(resolve, error => { + // Otherwise, best we can do is to recover from a backup if that exists, as such we + // move the DB to a different filename and try to load from backup. If that fails, + // a new empty DB is being created automatically. + // + // The final fallback is to use an in-memory DB which should only happen if the target + // folder is really not writeable for us. + // + return unlink(path) + .then(() => renameIgnoreError(this.toBackupPath(path), path)) + .then(() => this.doConnect(path)) + .then(undefined, error => { + this.logger.error(`[storage ${this.name}] open(): Unable to use backup due to ${error}`); - // This error code should only arise if another process is locking the same DB we - // want to open at that time. This typically never happens because a DB connection - // is limited per window. However, in the event of a window reload, it may be possible - // that the previous connection was not properly closed while the new connection is - // already established. - if (error.code === 'SQLITE_BUSY') { - this.logger.error(`[storage ${this.name}] open(): Retrying after ${SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT}ms due to SQLITE_BUSY`); - - // Retry after some time if the DB is busy - return timeout(SQLiteStorageDatabase.BUSY_OPEN_TIMEOUT).then(() => this.doConnect(path)).then(resolve, fallbackToInMemoryDatabase); - } - - // This error code indicates that even though the DB file exists, - // SQLite cannot open it and signals it is corrupt or not a DB. - if (error.code === 'SQLITE_CORRUPT' || error.code === 'SQLITE_NOTADB') { - this.logger.error(`[storage ${this.name}] open(): Unable to open DB due to ${error.code}`); - - // Move corrupt DB to a different filename and try to load from backup - // If that fails, a new empty DB is being created automatically - return rename(path, this.toCorruptPath(path)) - .then(() => renameIgnoreError(this.toBackupPath(path), path)) - .then(() => this.doConnect(path)) - .then(resolve, fallbackToInMemoryDatabase); - } - - // Otherwise give up and fallback to in-memory DB - return fallbackToInMemoryDatabase(error); - }); + // In case of any error to open the DB, use an in-memory + // DB so that we always have a valid DB to talk to. + return this.doConnect(SQLiteStorageDatabase.IN_MEMORY_PATH); + }); }); } @@ -566,12 +559,6 @@ export class SQLiteStorageDatabase implements IStorageDatabase { this.logger.error(msg); } - private toCorruptPath(path: string): string { - const randomSuffix = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 4); - - return `${path}.${randomSuffix}.corrupt`; - } - private doConnect(path: string): Promise { // TODO@Ben clean up performance markers @@ -598,7 +585,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // - create the DB if it does not exist yet // - validate that the DB is not corrupt (the open() call does not throw otherwise) mark('willSetupSQLiteSchema'); - this.exec(connection, [ + return this.exec(connection, [ 'PRAGMA user_version = 1;', 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' ].join('')).then(() => { @@ -621,7 +608,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { if (this.logger.isTracing) { connection.db.on('trace', sql => this.logger.trace(`[storage ${this.name}] Trace (event): ${sql}`)); } - }); + }, reject); }); } From 1b9efe4d16275c71ace2b86bc055ec557c5696e3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 11 Jan 2019 12:51:38 +0100 Subject: [PATCH 0177/1066] #66397 - Introduce a new type 'IExtension' and use in enablement service --- src/vs/code/node/cliProcessMain.ts | 11 +- .../common/extensionEnablementService.ts | 17 +- .../common/extensionManagement.ts | 148 ++---------------- .../common/extensionManagementUtil.ts | 9 ++ .../common/extensionNls.ts | 2 +- .../node/extensionGalleryService.ts | 3 +- .../node/extensionManagementIpc.ts | 7 +- .../node/extensionManagementService.ts | 73 ++++----- .../node/extensionManagementUtil.ts | 2 +- .../node/multiExtensionManagement.ts | 8 +- .../extensionEnablementService.test.ts | 15 +- .../platform/extensions/common/extensions.ts | 139 +++++++++++++++- .../experiments/node/experimentService.ts | 5 +- .../experimentService.test.ts | 21 ++- .../parts/extensions/common/extensions.ts | 5 +- .../electron-browser/extensionEditor.ts | 5 +- .../electron-browser/extensionTipsService.ts | 5 +- .../electron-browser/extensionsActions.ts | 24 +-- .../electron-browser/extensionsViewlet.ts | 7 +- .../electron-browser/extensionsViews.ts | 15 +- .../runtimeExtensionsEditor.ts | 8 +- .../node/extensionsWorkbenchService.ts | 23 +-- .../extensionsActions.test.ts | 36 +++-- .../extensionsTipsService.test.ts | 7 +- .../electron-browser/extensionsViews.test.ts | 30 ++-- .../extensionsWorkbenchService.test.ts | 57 +++---- .../localizations.contribution.ts | 5 +- .../electron-browser/preferencesSearch.ts | 5 +- .../page/electron-browser/welcomePage.ts | 5 +- .../electron-browser/extensionService.ts | 6 +- .../electron-browser/workbenchIssueService.ts | 5 +- 31 files changed, 370 insertions(+), 338 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index a5b746f9eb3..ed0972812b4 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IExtensionManagementService, IExtensionGalleryService, IExtensionManifest, IGalleryExtension, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -38,6 +38,7 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { areSameExtensions, getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; +import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; const notFound = (id: string) => localize('notFound', "Extension '{0}' not found.", id); const notInstalled = (id: string) => localize('notInstalled', "Extension '{0}' is not installed.", id); @@ -94,7 +95,7 @@ class Main { } private async listExtensions(showVersions: boolean): Promise { - const extensions = await this.extensionManagementService.getInstalled(LocalExtensionType.User); + const extensions = await this.extensionManagementService.getInstalled(ExtensionType.User); extensions.forEach(e => console.log(getId(e.manifest, showVersions))); } @@ -134,7 +135,7 @@ class Main { } const [id, version] = getIdAndVersion(extension); - return this.extensionManagementService.getInstalled(LocalExtensionType.User) + return this.extensionManagementService.getInstalled(ExtensionType.User) .then(installed => this.extensionGalleryService.getExtension({ id }, version) .then(null, err => { if (err.responseText) { @@ -184,7 +185,7 @@ class Main { } const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - const installedExtensions = await this.extensionManagementService.getInstalled(LocalExtensionType.User); + const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User); const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0]; if (newer && !force) { @@ -223,7 +224,7 @@ class Main { return sequence(extensions.map(extension => () => { return getExtensionId(extension).then(id => { - return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(installed => { + return this.extensionManagementService.getInstalled(ExtensionType.User).then(installed => { const [extension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id })); if (!extension) { diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 2a43a37696b..4f8c849352c 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -6,12 +6,13 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, ILocalExtension, isIExtensionIdentifier, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, isIExtensionIdentifier, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isUndefinedOrNull } from 'vs/base/common/types'; +import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled'; const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; @@ -76,7 +77,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension return result; } - getEnablementState(extension: ILocalExtension): EnablementState { + getEnablementState(extension: IExtension): EnablementState { if (this._isExtensionDisabledInEnvironment(extension)) { return EnablementState.Disabled; } @@ -96,17 +97,17 @@ export class ExtensionEnablementService extends Disposable implements IExtension return EnablementState.Enabled; } - canChangeEnablement(extension: ILocalExtension): boolean { + canChangeEnablement(extension: IExtension): boolean { if (extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) { return false; } - if (extension.type === LocalExtensionType.User && this.environmentService.disableExtensions) { + if (extension.type === ExtensionType.User && this.environmentService.disableExtensions) { return false; } return true; } - setEnablement(arg: ILocalExtension | IExtensionIdentifier, newState: EnablementState): Promise { + setEnablement(arg: IExtension | IExtensionIdentifier, newState: EnablementState): Promise { let identifier: IExtensionIdentifier; if (isIExtensionIdentifier(arg)) { identifier = arg; @@ -148,14 +149,14 @@ export class ExtensionEnablementService extends Disposable implements IExtension return Promise.resolve(true); } - isEnabled(extension: ILocalExtension): boolean { + isEnabled(extension: IExtension): boolean { const enablementState = this.getEnablementState(extension); return enablementState === EnablementState.WorkspaceEnabled || enablementState === EnablementState.Enabled; } - private _isExtensionDisabledInEnvironment(extension: ILocalExtension): boolean { + private _isExtensionDisabledInEnvironment(extension: IExtension): boolean { if (this.allUserExtensionsDisabled) { - return extension.type === LocalExtensionType.User; + return extension.type === ExtensionType.User; } const disabledExtensions = this.environmentService.disableExtensions; if (Array.isArray(disabledExtensions)) { diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index ff5a8cfff1b..0f02bc86468 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -7,135 +7,14 @@ import { localize } from 'vs/nls'; import { Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { ILocalization } from 'vs/platform/localizations/common/localizations'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); -export interface ICommand { - command: string; - title: string; - category?: string; -} - -export interface IConfigurationProperty { - description: string; - type: string | string[]; - default?: any; -} - -export interface IConfiguration { - properties: { [key: string]: IConfigurationProperty; }; -} - -export interface IDebugger { - label?: string; - type: string; - runtime: string; -} - -export interface IGrammar { - language: string; -} - -export interface IJSONValidation { - fileMatch: string; - url: string; -} - -export interface IKeyBinding { - command: string; - key: string; - when?: string; - mac?: string; - linux?: string; - win?: string; -} - -export interface ILanguage { - id: string; - extensions: string[]; - aliases: string[]; -} - -export interface IMenu { - command: string; - alt?: string; - when?: string; - group?: string; -} - -export interface ISnippet { - language: string; -} - -export interface ITheme { - label: string; -} - -export interface IViewContainer { - id: string; - title: string; -} - -export interface IView { - id: string; - name: string; -} - -export interface IColor { - id: string; - description: string; - defaults: { light: string, dark: string, highContrast: string }; -} - -export interface IExtensionContributions { - commands?: ICommand[]; - configuration?: IConfiguration | IConfiguration[]; - debuggers?: IDebugger[]; - grammars?: IGrammar[]; - jsonValidation?: IJSONValidation[]; - keybindings?: IKeyBinding[]; - languages?: ILanguage[]; - menus?: { [context: string]: IMenu[] }; - snippets?: ISnippet[]; - themes?: ITheme[]; - iconThemes?: ITheme[]; - viewsContainers?: { [location: string]: IViewContainer[] }; - views?: { [location: string]: IView[] }; - colors?: IColor[]; - localizations?: ILocalization[]; -} - -export type ExtensionKind = 'ui' | 'workspace'; - -export interface IExtensionManifest { - name: string; - publisher: string; - version: string; - engines: { vscode: string }; - displayName?: string; - description?: string; - main?: string; - icon?: string; - categories?: string[]; - keywords?: string[]; - activationEvents?: string[]; - extensionDependencies?: string[]; - extensionPack?: string[]; - extensionKind?: ExtensionKind; - contributes?: IExtensionContributions; - repository?: { - url: string; - }; - bugs?: { - url: string; - }; -} - export interface IGalleryExtensionProperties { dependencies?: string[]; extensionPack?: string[]; @@ -211,18 +90,9 @@ export interface IGalleryMetadata { publisherDisplayName: string; } -export const enum LocalExtensionType { - System, - User -} - -export interface ILocalExtension { - type: LocalExtensionType; - identifier: IExtensionIdentifier; - galleryIdentifier: IExtensionIdentifier; - manifest: IExtensionManifest; +export interface ILocalExtension extends IExtension { + manifest: IExtensionManifest; /** TODO:@sandy081 - remove the readonly relaxment */ metadata: IGalleryMetadata; - location: URI; readmeUrl: URI | null; changelogUrl: URI | null; } @@ -325,12 +195,12 @@ export interface IExtensionManagementService { onDidUninstallExtension: Event; zip(extension: ILocalExtension): Promise; - unzip(zipLocation: URI, type: LocalExtensionType): Promise; + unzip(zipLocation: URI, type: ExtensionType): Promise; install(vsix: URI): Promise; installFromGallery(extension: IGalleryExtension): Promise; uninstall(extension: ILocalExtension, force?: boolean): Promise; reinstallFromGallery(extension: ILocalExtension): Promise; - getInstalled(type?: LocalExtensionType): Promise; + getInstalled(type?: ExtensionType): Promise; getExtensionsReport(): Promise; updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise; @@ -379,17 +249,17 @@ export interface IExtensionEnablementService { /** * Returns the enablement state for the given extension */ - getEnablementState(extension: ILocalExtension): EnablementState; + getEnablementState(extension: IExtension): EnablementState; /** * Returns `true` if the enablement can be changed. */ - canChangeEnablement(extension: ILocalExtension): boolean; + canChangeEnablement(extension: IExtension): boolean; /** * Returns `true` if the given extension identifier is enabled. */ - isEnabled(extension: ILocalExtension): boolean; + isEnabled(extension: IExtension): boolean; /** * Enable or disable the given extension. @@ -400,7 +270,7 @@ export interface IExtensionEnablementService { * * Throws error if enablement is requested for workspace and there is no workspace */ - setEnablement(extension: ILocalExtension, state: EnablementState): Promise; + setEnablement(extension: IExtension, state: EnablementState): Promise; } export interface IExtensionsConfigContent { diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 445599f329b..a766977aa2f 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -5,6 +5,7 @@ import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { compareIgnoreCase } from 'vs/base/common/strings'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean { if (a.uuid && b.uuid) { @@ -38,6 +39,14 @@ export function getIdFromLocalExtensionId(localExtensionId: string): string { return adoptToGalleryExtensionId(localExtensionId); } +export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string { + return getLocalExtensionId(extension.identifier.id, version); +} + +export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string { + return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version); +} + export function getLocalExtensionId(id: string, version: string): string { return `${id}-${version}`; } diff --git a/src/vs/platform/extensionManagement/common/extensionNls.ts b/src/vs/platform/extensionManagement/common/extensionNls.ts index 14516cfc735..92340624e2a 100644 --- a/src/vs/platform/extensionManagement/common/extensionNls.ts +++ b/src/vs/platform/extensionManagement/common/extensionNls.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { cloneAndChange } from 'vs/base/common/objects'; -import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; const nlsRegex = /^%([\w\d.-]+)%$/i; diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index f00b365e842..77a3b6ce77f 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, IExtensionManifest, 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 } 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'; @@ -24,6 +24,7 @@ import { generateUuid, isUUID } from 'vs/base/common/uuid'; import { values } from 'vs/base/common/map'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; interface IRawGalleryExtensionFile { assetType: string; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index a7636486c47..a816fe8f8ab 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc'; -import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, LocalExtensionType, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from '../common/extensionManagement'; import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { cloneAndChange } from 'vs/base/common/objects'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -90,7 +91,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('zip', [extension]).then(result => URI.revive(result))); } - unzip(zipLocation: URI, type: LocalExtensionType): Promise { + unzip(zipLocation: URI, type: ExtensionType): Promise { return Promise.resolve(this.channel.call('unzip', [zipLocation, type])); } @@ -110,7 +111,7 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])); } - getInstalled(type: LocalExtensionType | null = null): Promise { + getInstalled(type: ExtensionType | null = null): Promise { return Promise.resolve(this.channel.call('getInstalled', [type])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 69e32ce2152..19b7a603188 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -12,8 +12,8 @@ import { flatten } from 'vs/base/common/arrays'; import { extract, ExtractError, zip, IFile } from 'vs/platform/node/zip'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, - IGalleryExtension, IExtensionManifest, IGalleryMetadata, - InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, LocalExtensionType, + IGalleryExtension, IGalleryMetadata, + InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, StatisticType, IExtensionIdentifier, IReportedExtension, @@ -21,7 +21,7 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } 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'; @@ -44,6 +44,7 @@ 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 } from 'vs/platform/extensions/common/extensions'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -155,7 +156,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(path => URI.file(path)); } - unzip(zipLocation: URI, type: LocalExtensionType): Promise { + unzip(zipLocation: URI, type: ExtensionType): Promise { this.logService.trace('ExtensionManagementService#unzip', zipLocation.toString()); return this.install(zipLocation, type); } @@ -186,7 +187,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } - install(vsix: URI, type: LocalExtensionType = LocalExtensionType.User): Promise { + install(vsix: URI, type: ExtensionType = ExtensionType.User): Promise { this.logService.trace('ExtensionManagementService#install', vsix.toString()); return createCancelablePromise(token => { return this.downloadVsix(vsix).then(downloadLocation => { @@ -201,7 +202,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.removeIfExists(identifier.id).then( () => { const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - return this.getInstalled(LocalExtensionType.User) + return this.getInstalled(ExtensionType.User) .then(installedExtensions => { const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0]; return newer ? this.uninstall(newer, true) : undefined; @@ -239,12 +240,12 @@ export class ExtensionManagementService extends Disposable implements IExtension } private removeIfExists(id: string): Promise { - return this.getInstalled(LocalExtensionType.User) + return this.getInstalled(ExtensionType.User) .then(installed => installed.filter(i => i.identifier.id === id)[0]) .then(existing => existing ? this.removeExtension(existing, 'existing') : undefined); } - private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata | null, type: LocalExtensionType, token: CancellationToken): Promise { + private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata | null, type: ExtensionType, token: CancellationToken): Promise { return this.toNonCancellablePromise(this.getInstalled() .then(installed => { const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed); @@ -296,7 +297,7 @@ export class ExtensionManagementService extends Disposable implements IExtension cancellablePromise = createCancelablePromise(token => { cancellationToken = token; return new Promise((c, e) => { successCallback = c; errorCallback = e; }); }); this.installingExtensions.set(key, cancellablePromise); try { - const installed = await this.getInstalled(LocalExtensionType.User); + const installed = await this.getInstalled(ExtensionType.User); const existingExtension = installed.filter(i => areSameExtensions(i.galleryIdentifier, extension.identifier))[0]; if (existingExtension) { operation = InstallOperation.Update; @@ -306,7 +307,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } this.downloadInstallableExtension(extension, operation) - .then(installableExtension => this.installExtension(installableExtension, LocalExtensionType.User, cancellationToken) + .then(installableExtension => this.installExtension(installableExtension, ExtensionType.User, cancellationToken) .then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local))) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) .then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) @@ -399,7 +400,7 @@ export class ExtensionManagementService extends Disposable implements IExtension error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_DOWNLOADING))); } - private installExtension(installableExtension: InstallableExtension, type: LocalExtensionType, token: CancellationToken): Promise { + private installExtension(installableExtension: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { return this.unsetUninstalledAndGetLocal(installableExtension.id) .then( local => { @@ -425,7 +426,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.unsetUninstalled(id) .then(() => { this.logService.info('Removed the extension from uninstalled list:', id); - return this.getInstalled(LocalExtensionType.User); + return this.getInstalled(ExtensionType.User); }) .then(installed => installed.filter(i => i.identifier.id === id)[0]); } @@ -433,8 +434,8 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private extractAndInstall({ zipPath, id, metadata }: InstallableExtension, type: LocalExtensionType, token: CancellationToken): Promise { - const location = type === LocalExtensionType.User ? this.extensionsPath : this.systemExtensionsPath; + private extractAndInstall({ zipPath, id, metadata }: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { + const location = type === ExtensionType.User ? this.extensionsPath : this.systemExtensionsPath; const tempPath = path.join(location, `.${id}`); const extensionPath = path.join(location, id); return pfs.rimraf(extensionPath) @@ -521,7 +522,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private rollback(extensions: IGalleryExtension[]): Promise { - return this.getInstalled(LocalExtensionType.User) + return this.getInstalled(ExtensionType.User) .then(installed => Promise.all(installed.filter(local => extensions.some(galleryExtension => local.identifier.id === getLocalExtensionIdFromGallery(galleryExtension, galleryExtension.version))) // Only check id (pub.name-version) because we want to rollback the exact version .map(local => this.uninstall(local, true)))) @@ -530,7 +531,7 @@ export class ExtensionManagementService extends Disposable implements IExtension uninstall(extension: ILocalExtension, force = false): Promise { this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); - return this.toNonCancellablePromise(this.getInstalled(LocalExtensionType.User) + return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User) .then(installed => { const extensionsToUninstall = installed .filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name); @@ -704,14 +705,14 @@ export class ExtensionManagementService extends Disposable implements IExtension this._onDidUninstallExtension.fire({ identifier: extension.identifier, error: errorcode }); } - getInstalled(type: LocalExtensionType | null = null): Promise { + getInstalled(type: ExtensionType | null = null): Promise { const promises: Promise[] = []; - if (type === null || type === LocalExtensionType.System) { + if (type === null || type === ExtensionType.System) { promises.push(this.scanSystemExtensions().then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_SYS_EXTENSIONS)))); } - if (type === null || type === LocalExtensionType.User) { + if (type === null || type === ExtensionType.User) { promises.push(this.scanUserExtensions(true).then(null, e => Promise.reject(new ExtensionManagementError(this.joinErrors(e).message, ERROR_SCANNING_USER_EXTENSIONS)))); } @@ -720,7 +721,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private scanSystemExtensions(): Promise { this.logService.trace('Started scanning system extensions'); - const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, LocalExtensionType.System) + const systemExtensionsPromise = this.scanExtensions(this.systemExtensionsPath, ExtensionType.System) .then(result => { this.logService.info('Scanned system extensions:', result.length); return result; @@ -733,7 +734,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const devSystemExtensionsPromise = this.getDevSystemExtensionsList() .then(devSystemExtensionsList => { if (devSystemExtensionsList.length) { - return this.scanExtensions(this.devSystemExtensionsPath, LocalExtensionType.System) + return this.scanExtensions(this.devSystemExtensionsPath, ExtensionType.System) .then(result => { this.logService.info('Scanned dev system extensions:', result.length); return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.galleryIdentifier, { id }))); @@ -748,7 +749,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private scanUserExtensions(excludeOutdated: boolean): Promise { this.logService.trace('Started scanning user extensions'); - return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, LocalExtensionType.User)]) + return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) .then(([uninstalled, extensions]) => { extensions = extensions.filter(e => !uninstalled[e.identifier.id]); if (excludeOutdated) { @@ -760,15 +761,15 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private scanExtensions(root: string, type: LocalExtensionType): Promise { + private scanExtensions(root: string, type: ExtensionType): Promise { const limiter = new Limiter(10); return pfs.readdir(root) .then(extensionsFolders => Promise.all(extensionsFolders.map(extensionFolder => limiter.queue(() => this.scanExtension(extensionFolder, root, type))))) .then(extensions => extensions.filter(e => e && e.identifier)); } - private scanExtension(folderName: string, root: string, type: LocalExtensionType): Promise { - if (type === LocalExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user extension folder starting with `.` + private scanExtension(folderName: string, root: string, type: ExtensionType): Promise { + if (type === ExtensionType.User && folderName.indexOf('.') === 0) { // Do not consider user extension folder starting with `.` return Promise.resolve(null); } const extensionPath = path.join(root, folderName); @@ -779,13 +780,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : null; const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null; - if (manifest.extensionDependencies) { - manifest.extensionDependencies = manifest.extensionDependencies.map(id => adoptToGalleryExtensionId(id)); - } - if (manifest.extensionPack) { - manifest.extensionPack = manifest.extensionPack.map(id => adoptToGalleryExtensionId(id)); - } - const identifier = { id: type === LocalExtensionType.System ? folderName : getLocalExtensionIdFromManifest(manifest), uuid: metadata ? metadata.id : null }; + const identifier = { id: type === ExtensionType.System ? folderName : getLocalExtensionIdFromManifest(manifest), uuid: metadata ? metadata.id : null }; const galleryIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: identifier.uuid }; return { type, identifier, galleryIdentifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl }; })) @@ -799,7 +794,7 @@ export class ExtensionManagementService extends Disposable implements IExtension private removeUninstalledExtensions(): Promise { return this.getUninstalledExtensions() - .then(uninstalled => this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions + .then(uninstalled => this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions .then(extensions => { const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[e.identifier.id]); return Promise.all(toRemove.map(e => this.extensionLifecycle.postUninstall(e).then(() => this.removeUninstalledExtension(e)))); @@ -808,7 +803,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private removeOutdatedExtensions(): Promise { - return this.scanExtensions(this.extensionsPath, LocalExtensionType.User) // All user extensions + return this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions .then(extensions => { const toRemove: ILocalExtension[] = []; @@ -966,12 +961,4 @@ export class ExtensionManagementService extends Disposable implements IExtension */ this.telemetryService.publicLog(eventName, assign(extensionData, { success: !error, duration, errorcode })); } -} - -export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string { - return getLocalExtensionId(extension.identifier.id, version); -} - -export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string { - return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version); -} +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts index 74de3958a6c..a0097deab0b 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts @@ -5,9 +5,9 @@ import * as semver from 'semver'; import { adoptToGalleryExtensionId, LOCAL_EXTENSION_ID_REGEX } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { buffer } from 'vs/platform/node/zip'; import { localize } from 'vs/nls'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: string, version: string | null } { const matches = LOCAL_EXTENSION_ID_REGEX.exec(localExtensionId); diff --git a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts index bfa6f5624a8..3cd5f0acc66 100644 --- a/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts +++ b/src/vs/platform/extensionManagement/node/multiExtensionManagement.ts @@ -5,11 +5,11 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { - IExtensionManagementService, ILocalExtension, IGalleryExtension, LocalExtensionType, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, + IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata, IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { flatten } from 'vs/base/common/arrays'; -import { isUIExtension } from 'vs/platform/extensions/common/extensions'; +import { isUIExtension, ExtensionType } 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'; @@ -45,7 +45,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidUninstallExtension); return emitter; }, new EventMultiplexer())).event; } - getInstalled(type?: LocalExtensionType): Promise { + getInstalled(type?: ExtensionType): Promise { return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type))) .then(result => flatten(result)); } @@ -78,7 +78,7 @@ export class MultiExtensionManagementService extends Disposable implements IExte throw new Error('Not Supported'); } - unzip(zipLocation: URI, type: LocalExtensionType): Promise { + unzip(zipLocation: URI, type: ExtensionType): Promise { return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.unzip(zipLocation, type))).then(([extensionIdentifier]) => extensionIdentifier); } diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 70051896e61..da5256fefa4 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as sinon from 'sinon'; -import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, IExtensionContributions, ILocalExtension, LocalExtensionType, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionEnablementService, DidUninstallExtensionEvent, EnablementState, ILocalExtension, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; @@ -12,6 +12,8 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; +import { IExtensionContributions, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { isUndefinedOrNull } from 'vs/base/common/types'; function storageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -422,16 +424,14 @@ suite('ExtensionEnablementService Test', () => { test('test canChangeEnablement return true for system extensions when extensions are disabled in environment', () => { instantiationService.stub(IEnvironmentService, { disableExtensions: true } as IEnvironmentService); testObject = new TestExtensionEnablementService(instantiationService); - const extension = aLocalExtension('pub.a'); - extension.type = LocalExtensionType.System; + const extension = aLocalExtension('pub.a', undefined, ExtensionType.System); assert.equal(testObject.canChangeEnablement(extension), true); }); test('test canChangeEnablement return false for system extensions when extension is disabled in environment', () => { instantiationService.stub(IEnvironmentService, { disableExtensions: ['pub.a'] } as IEnvironmentService); testObject = new TestExtensionEnablementService(instantiationService); - const extension = aLocalExtension('pub.a'); - extension.type = LocalExtensionType.System; + const extension = aLocalExtension('pub.a', undefined, ExtensionType.System); assert.equal(testObject.canChangeEnablement(extension), true); }); @@ -448,8 +448,9 @@ suite('ExtensionEnablementService Test', () => { }); -function aLocalExtension(id: string, contributes?: IExtensionContributions): ILocalExtension { +function aLocalExtension(id: string, contributes?: IExtensionContributions, type?: ExtensionType): ILocalExtension { const [publisher, name] = id.split('.'); + type = isUndefinedOrNull(type) ? ExtensionType.User : type; return Object.create({ identifier: { id }, galleryIdentifier: { id, uuid: undefined }, @@ -458,6 +459,6 @@ function aLocalExtension(id: string, contributes?: IExtensionContributions): ILo publisher, contributes }, - type: LocalExtensionType.User + type }); } diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 78ee711a9ae..fe10d9a9c07 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -4,15 +4,152 @@ *--------------------------------------------------------------------------------------------*/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } 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'; export const MANIFEST_CACHE_FOLDER = 'CachedExtensions'; export const USER_MANIFEST_CACHE_FILE = 'user'; export const BUILTIN_MANIFEST_CACHE_FILE = 'builtin'; +export interface ICommand { + command: string; + title: string; + category?: string; +} + +export interface IConfigurationProperty { + description: string; + type: string | string[]; + default?: any; +} + +export interface IConfiguration { + properties: { [key: string]: IConfigurationProperty; }; +} + +export interface IDebugger { + label?: string; + type: string; + runtime?: string; +} + +export interface IGrammar { + language: string; +} + +export interface IJSONValidation { + fileMatch: string; + url: string; +} + +export interface IKeyBinding { + command: string; + key: string; + when?: string; + mac?: string; + linux?: string; + win?: string; +} + +export interface ILanguage { + id: string; + extensions: string[]; + aliases: string[]; +} + +export interface IMenu { + command: string; + alt?: string; + when?: string; + group?: string; +} + +export interface ISnippet { + language: string; +} + +export interface ITheme { + label: string; +} + +export interface IViewContainer { + id: string; + title: string; +} + +export interface IView { + id: string; + name: string; +} + +export interface IColor { + id: string; + description: string; + defaults: { light: string, dark: string, highContrast: string }; +} + +export interface IExtensionContributions { + commands?: ICommand[]; + configuration?: IConfiguration | IConfiguration[]; + debuggers?: IDebugger[]; + grammars?: IGrammar[]; + jsonValidation?: IJSONValidation[]; + keybindings?: IKeyBinding[]; + languages?: ILanguage[]; + menus?: { [context: string]: IMenu[] }; + snippets?: ISnippet[]; + themes?: ITheme[]; + iconThemes?: ITheme[]; + viewsContainers?: { [location: string]: IViewContainer[] }; + views?: { [location: string]: IView[] }; + colors?: IColor[]; + localizations?: ILocalization[]; +} + +export type ExtensionKind = 'ui' | 'workspace'; + +export interface IExtensionIdentifier { + id: string; + uuid?: string; +} + +export interface IExtensionManifest { + readonly name: string; + readonly displayName?: string; + readonly publisher: string; + readonly version: string; + readonly engines: { vscode: string }; + readonly description?: string; + readonly main?: string; + readonly icon?: string; + readonly categories?: string[]; + readonly keywords?: string[]; + readonly activationEvents?: string[]; + readonly extensionDependencies?: string[]; + readonly extensionPack?: string[]; + readonly extensionKind?: ExtensionKind; + readonly contributes?: IExtensionContributions; + readonly repository?: { url: string; }; + readonly bugs?: { url: string; }; + readonly enableProposedApi?: boolean; +} + +export const enum ExtensionType { + System, + User +} + +export interface IExtension { + readonly type: ExtensionType; + readonly identifier: IExtensionIdentifier; + readonly galleryIdentifier: IExtensionIdentifier; /** This should be removed */ + readonly manifest: IExtensionManifest; + readonly location: URI; +} + const uiExtensions = new Set(); uiExtensions.add('msjsdiag.debugger-for-chrome'); diff --git a/src/vs/workbench/parts/experiments/node/experimentService.ts b/src/vs/workbench/parts/experiments/node/experimentService.ts index 1081f00d813..c1829068d55 100644 --- a/src/vs/workbench/parts/experiments/node/experimentService.ts +++ b/src/vs/workbench/parts/experiments/node/experimentService.ts @@ -11,7 +11,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IRequestService } from 'vs/platform/request/node/request'; import { language } from 'vs/base/common/platform'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -23,6 +23,7 @@ import { WorkspaceStats } from 'vs/workbench/parts/stats/node/workspaceStats'; import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; interface IExperimentStorageState { enabled: boolean; @@ -370,7 +371,7 @@ export class ExperimentService extends Disposable implements IExperimentService let extensionsCheckPromise = Promise.resolve(true); const installedExtensions = condition.installedExtensions; if (installedExtensions) { - extensionsCheckPromise = this.extensionManagementService.getInstalled(LocalExtensionType.User).then(locals => { + extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => { let includesCheck = true; let excludesCheck = true; const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`); diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts index fae51e767ad..e458acc70f6 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts @@ -10,9 +10,9 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices'; import { IExtensionManagementService, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, - IExtensionEnablementService, ILocalExtension, LocalExtensionType + IExtensionEnablementService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { Emitter } from 'vs/base/common/event'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { URLService } from 'vs/platform/url/common/urlService'; @@ -26,6 +26,8 @@ import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; +import { getLocalExtensionIdFromManifest, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; interface ExperimentSettings { enabled?: boolean; @@ -40,12 +42,15 @@ let experimentData: { [i: string]: any } = { const local = aLocalExtension('installedExtension1', { version: '1.0.0' }); function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - const localExtension = Object.create({ manifest: {} }); - assign(localExtension, { type: LocalExtensionType.User, manifest: {}, location: URI.file(`pub.${name}`) }, properties); - assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); - localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; - localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; - return localExtension; + manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); + properties = assign({ + type: ExtensionType.User, + location: URI.file(`pub.${name}`), + identifier: { id: getLocalExtensionIdFromManifest(manifest) }, + galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + }, properties); + return Object.create({ manifest, ...properties }); } export class TestExperimentService extends ExperimentService { diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 4420d4bffda..5c8339ab05a 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -7,12 +7,13 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; -import { IQueryOptions, IExtensionManifest, LocalExtensionType, EnablementState, ILocalExtension, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IQueryOptions, EnablementState, ILocalExtension, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; export const VIEWLET_ID = 'workbench.view.extensions'; export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); @@ -29,7 +30,7 @@ export const enum ExtensionState { } export interface IExtension { - type?: LocalExtensionType; + type?: ExtensionType; state: ExtensionState; name: string; displayName: string; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index f62edf65327..45f5218b292 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -20,7 +20,8 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionManifest, IKeyBinding, IView, IExtensionTipsService, LocalExtensionType, IViewContainer } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions'; 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'; @@ -291,7 +292,7 @@ export class ExtensionEditor extends BaseEditor { this.name.textContent = extension.displayName; this.identifier.textContent = extension.identifier.id; this.preview.style.display = extension.preview ? 'inherit' : 'none'; - this.builtin.style.display = extension.type === LocalExtensionType.System ? 'inherit' : 'none'; + this.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none'; this.publisher.textContent = extension.publisherDisplayName; this.description.textContent = extension.description; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index ff2e944baae..6b17eadcff6 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -10,7 +10,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { match } from 'vs/base/common/glob'; import * as json from 'vs/base/common/json'; import { - IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, LocalExtensionType, EXTENSION_IDENTIFIER_PATTERN, + IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, ExtensionRecommendationReason, EXTENSION_IDENTIFIER_PATTERN, IExtensionsConfigContent, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IModelService } from 'vs/editor/common/services/modelService'; @@ -44,6 +44,7 @@ import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/e import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -419,7 +420,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } - this.extensionsService.getInstalled(LocalExtensionType.User).then(local => { + this.extensionsService.getInstalled(ExtensionType.User).then(local => { const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, { id: getGalleryExtensionIdFromLocal(local) }))); if (!recommendations.length) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index f9c2e0776b1..4de31acba3b 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 { LocalExtensionType, 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, IExtensionManagementServerService, 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 } from 'vs/platform/extensions/common/extensions'; +import { isUIExtension, ExtensionIdentifier, 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'; @@ -145,7 +145,7 @@ export class InstallAction extends ExtensionAction { } update(): void { - if (!this.extension || this.extension.type === LocalExtensionType.System) { + if (!this.extension || this.extension.type === ExtensionType.System) { this.enabled = false; this.class = InstallAction.Class; this.label = InstallAction.INSTALL_LABEL; @@ -233,7 +233,7 @@ export class UninstallAction extends ExtensionAction { return; } - if (installedExtensions[0].type !== LocalExtensionType.User) { + if (installedExtensions[0].type !== ExtensionType.User) { this.enabled = false; return; } @@ -275,7 +275,7 @@ export class CombinedInstallAction extends ExtensionAction { this.installAction.update(); this.uninstallAction.update(); - if (!this.extension || this.extension.type === LocalExtensionType.System) { + if (!this.extension || this.extension.type === ExtensionType.System) { this.enabled = false; this.class = CombinedInstallAction.NoExtensionClass; } else if (this.extension.state === ExtensionState.Installing) { @@ -345,7 +345,7 @@ export class UpdateAction extends ExtensionAction { return; } - if (this.extension.type !== LocalExtensionType.User) { + if (this.extension.type !== ExtensionType.User) { this.enabled = false; this.class = UpdateAction.DisabledClass; this.label = this.getUpdateLabel(); @@ -2284,11 +2284,11 @@ export class DisableAllAction extends Action { } private update(): void { - this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) && !!e.local && this.extensionEnablementService.canChangeEnablement(e.local)); + this.enabled = this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled) && !!e.local && this.extensionEnablementService.canChangeEnablement(e.local)); } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User), EnablementState.Disabled); + return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.Disabled); } dispose(): void { @@ -2316,11 +2316,11 @@ export class DisableAllWorkpsaceAction extends Action { } private update(): void { - this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled)); + this.enabled = this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User && (e.enablementState === EnablementState.Enabled || e.enablementState === EnablementState.WorkspaceEnabled)); } run(): Promise { - return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === LocalExtensionType.User), EnablementState.WorkspaceDisabled); + return this.extensionsWorkbenchService.setEnablement(this.extensionsWorkbenchService.local.filter(e => e.type === ExtensionType.User), EnablementState.WorkspaceDisabled); } dispose(): void { @@ -2482,7 +2482,7 @@ export class ReinstallAction extends Action { } get enabled(): boolean { - return this.extensionsWorkbenchService.local.filter(l => l.type === LocalExtensionType.User && l.local).length > 0; + return this.extensionsWorkbenchService.local.filter(l => l.type === ExtensionType.User && l.local).length > 0; } run(): Promise { @@ -2494,7 +2494,7 @@ export class ReinstallAction extends Action { return this.extensionsWorkbenchService.queryLocal() .then(local => { const entries = local - .filter(extension => extension.type === LocalExtensionType.User) + .filter(extension => extension.type === ExtensionType.User) .map(extension => { return { id: extension.identifier.id, diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 0b7e42e4907..5ee6810db7f 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -24,7 +24,7 @@ import { ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, EnableAutoUpdateAction, DisableAutoUpdateAction, ShowBuiltInExtensionsAction, InstallVSIXAction, ChangeGroupAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; -import { LocalExtensionType, IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionManagementServerService, IExtensionManagementServer, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionsInput } from 'vs/workbench/parts/extensions/common/extensionsInput'; import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, BuiltInExtensionsView, BuiltInThemesExtensionsView, BuiltInBasicsExtensionsView, GroupByServerExtensionsView, DefaultRecommendedExtensionsView } from './extensionsViews'; import { OpenGlobalSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; @@ -55,6 +55,7 @@ import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/work import { alert } from 'vs/base/browser/ui/aria/aria'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; interface SearchInputEvent extends Event { target: HTMLInputElement; @@ -323,7 +324,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio this.defaultRecommendedExtensionsContextKey.set(!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)); this.disposables.push(this.viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables)); - this.extensionManagementService.getInstalled(LocalExtensionType.User).then(result => { + this.extensionManagementService.getInstalled(ExtensionType.User).then(result => { this.hasInstalledExtensionsContextKey.set(result.length > 0); }); @@ -632,7 +633,7 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { return this.extensionsManagementService.getExtensionsReport().then(report => { const maliciousSet = getMaliciousExtensionsSet(report); - return this.extensionsManagementService.getInstalled(LocalExtensionType.User).then(installed => { + return this.extensionsManagementService.getInstalled(ExtensionType.User).then(installed => { const maliciousExtensions = installed .filter(e => maliciousSet.has(getGalleryExtensionIdFromLocal(e))); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index 4caf4f4da0e..d22fc4f1555 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -9,7 +9,7 @@ import { assign } from 'vs/base/common/objects'; import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; -import { SortBy, SortOrder, IQueryOptions, LocalExtensionType, IExtensionTipsService, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { SortBy, SortOrder, IQueryOptions, IExtensionTipsService, IExtensionRecommendation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -42,6 +42,7 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; import { IAction } from 'vs/base/common/actions'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; class ExtensionsViewState extends Disposable implements IExtensionsViewState { @@ -214,7 +215,7 @@ export class ExtensionsListView extends ViewletPanel { let result = await this.extensionsWorkbenchService.queryLocal(); result = result - .filter(e => e.type === LocalExtensionType.System && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)); + .filter(e => e.type === ExtensionType.System && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1)); if (showThemesOnly) { const themesExtensions = result.filter(e => { @@ -264,7 +265,7 @@ export class ExtensionsListView extends ViewletPanel { let result = await this.extensionsWorkbenchService.queryLocal(); result = result - .filter(e => e.type === LocalExtensionType.User + .filter(e => e.type === ExtensionType.User && (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local.manifest.categories || []).some(c => c.toLowerCase() === category)))); @@ -303,7 +304,7 @@ export class ExtensionsListView extends ViewletPanel { if (/@enabled/i.test(value)) { value = value ? value.replace(/@enabled/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase() : ''; - const local = (await this.extensionsWorkbenchService.queryLocal()).filter(e => e.type === LocalExtensionType.User); + const local = (await this.extensionsWorkbenchService.queryLocal()).filter(e => e.type === ExtensionType.User); const runningExtensions = await this.extensionService.getExtensions(); const result = local @@ -445,7 +446,7 @@ export class ExtensionsListView extends ViewletPanel { const value = query.value.replace(/@recommended:all/g, '').replace(/@recommended/g, '').trim().toLowerCase(); return this.extensionsWorkbenchService.queryLocal() - .then(result => result.filter(e => e.type === LocalExtensionType.User)) + .then(result => result.filter(e => e.type === ExtensionType.User)) .then(local => { const fileBasedRecommendations = this.tipsService.getFileBasedRecommendations(); const othersPromise = this.tipsService.getOtherRecommendations(); @@ -500,7 +501,7 @@ export class ExtensionsListView extends ViewletPanel { const value = query.value.replace(/@recommended/g, '').trim().toLowerCase(); return this.extensionsWorkbenchService.queryLocal() - .then(result => result.filter(e => e.type === LocalExtensionType.User)) + .then(result => result.filter(e => e.type === ExtensionType.User)) .then(local => { let fileBasedRecommendations = this.tipsService.getFileBasedRecommendations(); const othersPromise = this.tipsService.getOtherRecommendations(); @@ -790,7 +791,7 @@ export class DefaultRecommendedExtensionsView extends ExtensionsListView { return this.showEmptyModel(); } const model = await super.show(this.recommendedExtensionsQuery); - if (!this.extensionsWorkbenchService.local.some(e => e.type === LocalExtensionType.User)) { + if (!this.extensionsWorkbenchService.local.some(e => e.type === ExtensionType.User)) { // This is part of popular extensions view. Collapse if no installed extensions. this.setExpanded(model.length > 0); } diff --git a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts index 2e715498b87..4642d292181 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -23,7 +23,7 @@ import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { clipboard } from 'electron'; -import { LocalExtensionType, EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EnablementState } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { writeFile } from 'vs/base/node/pfs'; @@ -42,7 +42,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { join } from 'path'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -476,7 +476,7 @@ export class ReportExtensionIssueAction extends Action { }) { super(ReportExtensionIssueAction._id, ReportExtensionIssueAction._label, 'extension-action report-issue'); this.enabled = extension.marketplaceInfo - && extension.marketplaceInfo.type === LocalExtensionType.User + && extension.marketplaceInfo.type === ExtensionType.User && Boolean(extension.description.repository) && Boolean(extension.description.repository.url); const { url, task } = ReportExtensionIssueAction._generateNewIssueUrl(extension); @@ -499,7 +499,7 @@ export class ReportExtensionIssueAction extends Action { }): { url: string, task?: () => Promise } { let task: () => Promise; - let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === LocalExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; + let baseUrl = extension.marketplaceInfo && extension.marketplaceInfo.type === ExtensionType.User && extension.description.repository ? extension.description.repository.url : undefined; if (!!baseUrl) { baseUrl = `${baseUrl.indexOf('.git') !== -1 ? baseUrl.substr(0, baseUrl.length - 4) : baseUrl}/issues/new/`; } else { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index fd01f0e7dd1..50685c9f6fd 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -13,8 +13,8 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IPager, mapPager, singlePagePager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { - IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, IExtensionManifest, - InstallExtensionEvent, DidInstallExtensionEvent, LocalExtensionType, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService + IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, + InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -37,6 +37,7 @@ 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 } from 'vs/platform/extensions/common/extensions'; interface IExtensionStateProvider { (extension: Extension): T; @@ -57,7 +58,7 @@ class Extension implements IExtension { private fileService: IFileService ) { } - get type(): LocalExtensionType | undefined { + get type(): ExtensionType | undefined { return this.local ? this.local.type : undefined; } @@ -144,7 +145,7 @@ class Extension implements IExtension { } private get defaultIconUrl(): string { - if (this.type === LocalExtensionType.System) { + if (this.type === ExtensionType.System) { 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'); @@ -184,7 +185,7 @@ class Extension implements IExtension { } get outdated(): boolean { - return !!this.gallery && this.type === LocalExtensionType.User && semver.gt(this.latestVersion, this.version); + return !!this.gallery && this.type === ExtensionType.User && semver.gt(this.latestVersion, this.version); } get telemetryData(): any { @@ -226,7 +227,7 @@ class Extension implements IExtension { return true; } - return this.type === LocalExtensionType.System; + return this.type === ExtensionType.System; } getReadme(token: CancellationToken): Promise { @@ -241,7 +242,7 @@ class Extension implements IExtension { return this.fileService.resolveContent(this.local.readmeUrl, { encoding: 'utf8' }).then(content => content.value); } - if (this.type === LocalExtensionType.System) { + if (this.type === ExtensionType.System) { return Promise.resolve(`# ${this.displayName || this.name} **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. ## Features @@ -261,7 +262,7 @@ ${this.description} return true; } - return this.type === LocalExtensionType.System; + return this.type === ExtensionType.System; } getChangelog(token: CancellationToken): Promise { @@ -272,7 +273,7 @@ ${this.description} const changelogUrl = this.local && this.local.changelogUrl; if (!changelogUrl) { - if (this.type === LocalExtensionType.System) { + if (this.type === ExtensionType.System) { return Promise.resolve('Please check the [VS Code Release Notes](command:update.showCurrentReleaseNotes) for changes to the built-in extensions.'); } @@ -615,7 +616,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private syncWithGallery(): Promise { const ids: string[] = [], names: string[] = []; for (const installed of this.installed) { - if (installed.type === LocalExtensionType.User) { + if (installed.type === ExtensionType.User) { if (installed.uuid) { ids.push(installed.uuid); } else { @@ -818,7 +819,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return false; } const enable = enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled; - return (enable || i.type === LocalExtensionType.User) // Include all Extensions for enablement and only user extensions for disablement + return (enable || i.type === ExtensionType.User) // Include all Extensions for enablement and only user extensions for disablement && (options.dependencies || options.pack) && extensions.some(extension => (options.dependencies && extension.dependencies.some(id => areSameExtensions({ id }, i.identifier))) 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 e6166dc3dda..891cf0c6955 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 @@ -10,11 +10,11 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/commo import * as ExtensionsActions from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { - IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer, IExtensionContributions + IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { getGalleryExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -36,7 +36,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/ 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'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionContributions, ExtensionType } from 'vs/platform/extensions/common/extensions'; suite('ExtensionsActions Test', () => { @@ -153,7 +153,7 @@ suite('ExtensionsActions Test', () => { test('Test Install action when extension is system action', () => { const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.InstallAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -221,7 +221,7 @@ suite('ExtensionsActions Test', () => { test('Test Uninstall action when state is installed and is system extension', () => { const testObject: ExtensionsActions.UninstallAction = instantiationService.createInstance(ExtensionsActions.UninstallAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -280,7 +280,7 @@ suite('ExtensionsActions Test', () => { test('Test CombinedInstallAction when extension is system extension', () => { const testObject: ExtensionsActions.CombinedInstallAction = instantiationService.createInstance(ExtensionsActions.CombinedInstallAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -411,7 +411,7 @@ suite('ExtensionsActions Test', () => { test('Test UpdateAction when extension is installed outdated and system extension', () => { const testObject: ExtensionsActions.UpdateAction = instantiationService.createInstance(ExtensionsActions.UpdateAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); - const local = aLocalExtension('a', { version: '1.0.0' }, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', { version: '1.0.0' }, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -540,7 +540,7 @@ suite('ExtensionsActions Test', () => { test('Test ManageExtensionAction when extension is system extension', () => { const testObject: ExtensionsActions.ManageExtensionAction = instantiationService.createInstance(ExtensionsActions.ManageExtensionAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); return instantiationService.get(IExtensionsWorkbenchService).queryLocal() @@ -1341,13 +1341,15 @@ suite('ExtensionsActions Test', () => { }); function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - const localExtension = Object.create({ manifest: {} }); - assign(localExtension, { type: LocalExtensionType.User, manifest: {}, location: URI.file(`pub.${name}`) }, properties); - assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); - localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; - localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; - localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: undefined }; - return localExtension; + manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); + properties = assign({ + type: ExtensionType.User, + location: URI.file(`pub.${name}`), + identifier: { id: getLocalExtensionIdFromManifest(manifest) }, + galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + }, properties); + return Object.create({ manifest, ...properties }); } function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension { diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index 662d97098fe..fbe007ccbed 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -11,7 +11,7 @@ import * as os from 'os'; import * as uuid from 'vs/base/common/uuid'; import { mkdirp } from 'vs/base/node/pfs'; import { - IExtensionGalleryService, IGalleryExtensionAssets, IGalleryExtension, IExtensionManagementService, LocalExtensionType, + IExtensionGalleryService, IGalleryExtensionAssets, IGalleryExtension, IExtensionManagementService, IExtensionEnablementService, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; @@ -47,6 +47,7 @@ import { URLService } from 'vs/platform/url/common/urlService'; import { IExperimentService } from 'vs/workbench/parts/experiments/node/experimentService'; import { TestExperimentService } from 'vs/workbench/parts/experiments/test/electron-browser/experimentService.test'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -97,7 +98,7 @@ const mockExtensionGallery: IGalleryExtension[] = [ const mockExtensionLocal = [ { - type: LocalExtensionType.User, + type: ExtensionType.User, identifier: mockExtensionGallery[0].identifier, manifest: { name: mockExtensionGallery[0].name, @@ -110,7 +111,7 @@ const mockExtensionLocal = [ changelogUrl: 'some changelogUrl' }, { - type: LocalExtensionType.User, + type: ExtensionType.User, identifier: mockExtensionGallery[1].identifier, manifest: { name: mockExtensionGallery[1].name, diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index f9cf0f1488a..53a3b27a696 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -11,11 +11,11 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { - IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, IQueryOptions, + IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, IQueryOptions, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { getGalleryExtensionId, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -38,7 +38,7 @@ import { IExperimentService, ExperimentService, ExperimentState, ExperimentActio import { IRemoteAgentService } from 'vs/workbench/services/remote/node/remoteAgentService'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; suite('ExtensionsListView Tests', () => { @@ -55,8 +55,8 @@ suite('ExtensionsListView Tests', () => { const localDisabledTheme = aLocalExtension('first-disabled-extension', { categories: ['themes'] }); const localDisabledLanguage = aLocalExtension('second-disabled-extension', { categories: ['programming languages'] }); const localRandom = aLocalExtension('random-enabled-extension', { categories: ['random'] }); - const builtInTheme = aLocalExtension('my-theme', { contributes: { themes: ['my-theme'] } }, {}, LocalExtensionType.System); - const builtInBasic = aLocalExtension('my-lang', { contributes: { grammars: [{ language: 'my-language' }] } }, {}, LocalExtensionType.System); + const builtInTheme = aLocalExtension('my-theme', { contributes: { themes: ['my-theme'] } }, { type: ExtensionType.System }); + const builtInBasic = aLocalExtension('my-lang', { contributes: { grammars: [{ language: 'my-language' }] } }, { type: ExtensionType.System }); const workspaceRecommendationA = aGalleryExtension('workspace-recommendation-A'); const workspaceRecommendationB = aGalleryExtension('workspace-recommendation-B'); @@ -498,14 +498,16 @@ suite('ExtensionsListView Tests', () => { }); }); - function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}, type: LocalExtensionType = LocalExtensionType.User): ILocalExtension { - const localExtension = Object.create({ manifest: {} }); - assign(localExtension, { type, manifest: {}, location: URI.file(`pub.${name}`) }, properties); - assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); - localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; - localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; - localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: undefined }; - return localExtension; + function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { + manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); + properties = assign({ + type: ExtensionType.User, + location: URI.file(`pub.${name}`), + identifier: { id: getLocalExtensionIdFromManifest(manifest) }, + galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + }, properties); + return Object.create({ manifest, ...properties }); } function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension { diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 12e3140f7c2..466d98599e4 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 @@ -11,11 +11,11 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IExtensionsWorkbenchService, ExtensionState, AutoCheckUpdatesConfigurationKey, AutoUpdateConfigurationKey } from 'vs/workbench/parts/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/node/extensionsWorkbenchService'; import { - IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, LocalExtensionType, IGalleryExtension, + IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionManagementService, getLocalExtensionIdFromGallery, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { getGalleryExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; @@ -36,6 +36,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati 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'; suite('ExtensionsWorkbenchServiceTest', () => { @@ -164,7 +165,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { icon: 'localIcon1', extensionDependencies: ['pub.1', 'pub.2'], }, { - type: LocalExtensionType.User, + type: ExtensionType.User, readmeUrl: 'localReadmeUrl1', changelogUrl: 'localChangelogUrl1', location: URI.file('localPath1') @@ -175,7 +176,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { displayName: 'localDisplayName2', description: 'localDescription2', }, { - type: LocalExtensionType.System, + type: ExtensionType.System, readmeUrl: 'localReadmeUrl2', changelogUrl: 'localChangelogUrl2', }); @@ -186,7 +187,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(2, actuals.length); let actual = actuals[0]; - assert.equal(LocalExtensionType.User, actual.type); + assert.equal(ExtensionType.User, actual.type); assert.equal('local1', actual.name); assert.equal('localDisplayName1', actual.displayName); assert.equal('localpublisher1.local1', actual.identifier.id); @@ -205,7 +206,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.deepEqual(['pub.1', 'pub.2'], actual.dependencies); actual = actuals[1]; - assert.equal(LocalExtensionType.System, actual.type); + assert.equal(ExtensionType.System, actual.type); assert.equal('local2', actual.name); assert.equal('localDisplayName2', actual.displayName); assert.equal('localpublisher2.local2', actual.identifier.id); @@ -232,7 +233,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { icon: 'localIcon1', extensionDependencies: ['pub.1', 'pub.2'], }, { - type: LocalExtensionType.User, + type: ExtensionType.User, readmeUrl: 'localReadmeUrl1', changelogUrl: 'localChangelogUrl1', location: URI.file('localPath1') @@ -243,7 +244,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { displayName: 'localDisplayName2', description: 'localDescription2', }, { - type: LocalExtensionType.System, + type: ExtensionType.System, readmeUrl: 'localReadmeUrl2', changelogUrl: 'localChangelogUrl2', }); @@ -280,7 +281,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(2, actuals.length); let actual = actuals[0]; - assert.equal(LocalExtensionType.User, actual.type); + assert.equal(ExtensionType.User, actual.type); assert.equal('local1', actual.name); assert.equal('expectedDisplayName', actual.displayName); assert.equal('localpublisher1.local1', actual.identifier.id); @@ -299,7 +300,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.deepEqual(['pub.1'], actual.dependencies); actual = actuals[1]; - assert.equal(LocalExtensionType.System, actual.type); + assert.equal(ExtensionType.System, actual.type); assert.equal('local2', actual.name); assert.equal('localDisplayName2', actual.displayName); assert.equal('localpublisher2.local2', actual.identifier.id); @@ -358,7 +359,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test extension doesnot show outdated for system extensions', async () => { - const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', { version: '1.0.1' }, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { identifier: local.identifier, version: '1.0.2' }))); testObject = await aWorkbenchService(); @@ -368,7 +369,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test canInstall returns false for extensions with out gallery', async () => { - const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', { version: '1.0.1' }, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testObject = await aWorkbenchService(); const target = testObject.local[0]; @@ -380,7 +381,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test canInstall returns false for a system extension', async () => { - const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', { version: '1.0.1' }, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { identifier: local.identifier }))); testObject = await aWorkbenchService(); @@ -390,7 +391,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test canInstall returns true for extensions with gallery', async () => { - const local = aLocalExtension('a', { version: '1.0.1' }, { type: LocalExtensionType.User }); + const local = aLocalExtension('a', { version: '1.0.1' }, { type: ExtensionType.User }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension(local.manifest.name, { identifier: local.identifier }))); testObject = await aWorkbenchService(); @@ -443,7 +444,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test onchange event is triggered while uninstalling', async () => { - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testObject = await aWorkbenchService(); const target = sinon.spy(); @@ -456,7 +457,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test onchange event is triggered when uninstalling is finished', async () => { - const local = aLocalExtension('a', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('a', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testObject = await aWorkbenchService(); const target = sinon.spy(); @@ -584,7 +585,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test one level extension dependencies with in built dependencies', async () => { - const local = aLocalExtension('inbuilt', {}, { type: LocalExtensionType.System }); + const local = aLocalExtension('inbuilt', {}, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testObject = await aWorkbenchService(); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.inbuilt', 'pub.a'] }))); @@ -619,7 +620,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test more than one level of extension dependencies', async () => { - const local = aLocalExtension('c', { extensionDependencies: ['pub.d'] }, { type: LocalExtensionType.System }); + const local = aLocalExtension('c', { extensionDependencies: ['pub.d'] }, { type: ExtensionType.System }); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); testObject = await aWorkbenchService(); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a', {}, { dependencies: ['pub.b', 'pub.c'] }))); @@ -808,7 +809,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test system extensions can be disabled', async () => { - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', {}, { type: LocalExtensionType.System })]); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a', {}, { type: ExtensionType.System })]); testObject = await aWorkbenchService(); return testObject.setEnablement(testObject.local[0], EnablementState.Disabled) @@ -1181,13 +1182,15 @@ suite('ExtensionsWorkbenchServiceTest', () => { } function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension { - const localExtension = Object.create({ manifest: {} }); - assign(localExtension, { type: LocalExtensionType.User, manifest: {} }, properties); - assign(localExtension.manifest, { name, publisher: 'pub', version: '1.0.0' }, manifest); - localExtension.identifier = { id: getLocalExtensionIdFromManifest(localExtension.manifest) }; - localExtension.metadata = { id: localExtension.identifier.id, publisherId: localExtension.manifest.publisher, publisherDisplayName: 'somename' }; - localExtension.galleryIdentifier = { id: getGalleryExtensionIdFromLocal(localExtension), uuid: undefined }; - return localExtension; + manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest); + properties = assign({ + type: ExtensionType.User, + location: URI.file(`pub.${name}`), + identifier: { id: getLocalExtensionIdFromManifest(manifest) }, + galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } + }, properties); + return Object.create({ manifest, ...properties }); } const noAssets: IGalleryExtensionAssets = { diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts index b6cd57de315..6856801b960 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -15,7 +15,7 @@ import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/exte import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { IExtensionManagementService, DidInstallExtensionEvent, LocalExtensionType, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidInstallExtensionEvent, IExtensionGalleryService, IGalleryExtension, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; import Severity from 'vs/base/common/severity'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; @@ -29,6 +29,7 @@ import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewlet } from 'vs/work import { minimumTranslatedStrings } from 'vs/platform/node/minimalTranslations'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; // Register action to configure locale and related settings const registry = Registry.as(Extensions.WorkbenchActions); @@ -211,7 +212,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } private isLanguageInstalled(language: string): Promise { - return this.extensionManagementService.getInstalled(LocalExtensionType.User) + return this.extensionManagementService.getInstalled(ExtensionType.User) .then(installed => installed.some(i => i.manifest && i.manifest.contributes && i.manifest.contributes.localizations && i.manifest.contributes.localizations.length && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language))); } diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index b928db33ec3..fc8c467e942 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -17,11 +17,12 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IRequestService } from 'vs/platform/request/node/request'; import { asJson } from 'vs/base/node/request'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, LocalExtensionType, ILocalExtension, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { IPreferencesSearchService, ISearchProvider, IWorkbenchSettingsConfiguration } from 'vs/workbench/parts/preferences/common/preferences'; import { CancellationToken } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; export interface IEndpointDetails { urlBase: string; @@ -43,7 +44,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences super(); // This request goes to the shared process but results won't change during a window's lifetime, so cache the results. - this._installedExtensions = this.extensionManagementService.getInstalled(LocalExtensionType.User).then(exts => { + this._installedExtensions = this.extensionManagementService.getInstalled(ExtensionType.User).then(exts => { // Filter to enabled extensions that have settings return exts .filter(ext => this.extensionEnablementService.isEnabled(ext)) 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 d39d751ebb2..ce83aa005e0 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -24,7 +24,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; -import { IExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, EnablementState, LocalExtensionType, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionEnablementService, IExtensionManagementService, IExtensionGalleryService, IExtensionTipsService, EnablementState, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { used } from 'vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -41,6 +41,7 @@ import { TimeoutTimer } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFileService } from 'vs/platform/files/common/files'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; used(); @@ -465,7 +466,7 @@ class WelcomePage { return null; } return this.extensionManagementService.installFromGallery(extension) - .then(() => this.extensionManagementService.getInstalled(LocalExtensionType.User)) + .then(() => this.extensionManagementService.getInstalled(ExtensionType.User)) .then(installed => { const local = installed.filter(i => areSameExtensions(extension.identifier, i.galleryIdentifier))[0]; // TODO: Do this as part of the install to avoid multiple events. diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 5fbd532a00a..0d31a2088c5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -13,7 +13,7 @@ import * as perf from 'vs/base/common/performance'; import { isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { BetterMergeId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -29,7 +29,7 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -492,7 +492,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }); if (extensionsToDisable.length) { - return this._extensionManagementService.getInstalled(LocalExtensionType.User) + return this._extensionManagementService.getInstalled(ExtensionType.User) .then(installed => { const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); return Promise.all(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index ef84186551c..80c4ac2f824 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -7,11 +7,12 @@ import { IssueReporterStyles, IIssueService, IssueReporterData, ProcessExplorerD import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { textLinkForeground, inputBackground, inputBorder, inputForeground, buttonBackground, buttonHoverBackground, buttonForeground, inputValidationErrorBorder, foreground, inputActiveOptionBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, editorBackground, editorForeground, listHoverBackground, listHoverForeground, listHighlightForeground, textLinkActiveForeground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IExtensionManagementService, IExtensionEnablementService, LocalExtensionType } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { webFrame } from 'electron'; import { assign } from 'vs/base/common/objects'; import { IWorkbenchIssueService } from 'vs/workbench/services/issue/common/issue'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; export class WorkbenchIssueService implements IWorkbenchIssueService { _serviceBrand: any; @@ -25,7 +26,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { ) { } openReporter(dataOverrides: Partial = {}): Promise { - return this.extensionManagementService.getInstalled(LocalExtensionType.User).then(extensions => { + return this.extensionManagementService.getInstalled(ExtensionType.User).then(extensions => { const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension)); const extensionData: IssueReporterExtensionData[] = enabledExtensions.map(extension => { const { manifest } = extension; From 84b56fcd4a74f722c633cff8f1535b31af15d305 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Fri, 11 Jan 2019 14:54:48 +0100 Subject: [PATCH 0178/1066] fix typo --- .../parts/debug/test/electron-browser/debugModel.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts index d0314f97296..90b9c7a103f 100644 --- a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts @@ -91,7 +91,7 @@ suite('Debug - Model', () => { assert.equal(model.getBreakpoints().length, 0); }); - test('function brekapoints', () => { + test('function breakpoints', () => { model.addFunctionBreakpoint('foo', '1'); model.addFunctionBreakpoint('bar', '2'); model.renameFunctionBreakpoint('1', 'fooUpdated'); From a4d861e2c12b4dbd4cd8259cf08bc3972a91701a Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 15:30:23 +0100 Subject: [PATCH 0179/1066] do not show authority for workspace labels --- src/vs/platform/label/common/label.ts | 4 ++-- src/vs/workbench/services/label/common/labelService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 8de1893da38..a003d9d1dd1 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -8,7 +8,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { basenameOrAuthority } from 'vs/base/common/resources'; +import { basename as resourceBasename } from 'vs/base/common/resources'; import { isLinux } from 'vs/base/common/platform'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces'; import { localize } from 'vs/nls'; @@ -51,7 +51,7 @@ const LABEL_SERVICE_ID = 'label'; export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, workspaceHome: string): string { if (isSingleFolderWorkspaceIdentifier(workspace)) { - return basenameOrAuthority(workspace); + return resourceBasename(workspace); } // Workspace: Untitled if (isParent(workspace.configPath, workspaceHome, !isLinux /* ignore case */)) { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 146c7bd4b9d..d3fd3ea2945 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { isEqual, basenameOrAuthority } from 'vs/base/common/resources'; +import { isEqual, basenameOrAuthority, basename as resourceBasename } from 'vs/base/common/resources'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { tildify, getPathLabel } from 'vs/base/common/labels'; import { ltrim, startsWith } from 'vs/base/common/strings'; @@ -103,7 +103,7 @@ export class LabelService implements ILabelService { if (isSingleFolderWorkspaceIdentifier(workspace)) { // Folder on disk const formatter = this.findFormatter(workspace); - const label = options && options.verbose ? this.getUriLabel(workspace) : basenameOrAuthority(workspace); + const label = options && options.verbose ? this.getUriLabel(workspace) : resourceBasename(workspace); if (workspace.scheme === Schemas.file) { return label; } From b52a3280c11a25c57322be2a9226eded16c07b16 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 11 Jan 2019 15:47:57 +0100 Subject: [PATCH 0180/1066] file icons fixes #64894 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/media/tree.css | 1 + .../workbench/browser/parts/views/customView.ts | 2 ++ .../browser/parts/views/media/views.css | 13 ++++++++++++- src/vs/workbench/browser/parts/views/views.ts | 17 ++++++++++++++++- .../electron-browser/views/explorerView.ts | 3 +++ .../electron-browser/fileIconThemeData.ts | 6 +++++- 7 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 31046489dcd..ecd6078b7cd 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -193,7 +193,7 @@ class TreeRenderer implements IListRenderer { const findMatchHighlightColor = theme.getColor(editorFindMatchHighlight); if (findMatchHighlightColor) { collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); + collector.addRule(`.file-icon-themable-tree .monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); } const findMatchHighlightColorBorder = theme.getColor(editorFindMatchHighlightBorder); if (findMatchHighlightColorBorder) { collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); + collector.addRule(`.file-icon-themable-tree .monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); } const link = theme.getColor(textLinkForeground); if (link) { diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index b4f83027944..ec2880220b8 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* File icon themeable tree style */ +/* File icon themeable OLD tree style */ .file-icon-themable-tree .monaco-tree-row .content { display: flex; } @@ -50,6 +50,17 @@ display: none; } +/* File icons in trees */ + +.file-icon-themable-tree.align-icons-and-twisties .monaco-tl-twistie:not(.collapsible), +.file-icon-themable-tree.hide-arrows .monaco-tl-twistie { + background-image: none !important; + width: 0 !important; + margin-right: 0 !important; +} + +/* Misc */ + .monaco-workbench .tree-explorer-viewlet-tree-view { height: 100%; } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 44ff6623fd2..b0e7153d2f1 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/views'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -19,6 +19,8 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { localize } from 'vs/nls'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { values } from 'vs/base/common/map'; +import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { toggleClass, addClass } from 'vs/base/browser/dom'; function filterViewEvent(container: ViewContainer, event: Event): Event { return Event.chain(event) @@ -612,3 +614,16 @@ export class ViewsService extends Disposable implements IViewsService { return contextKey; } } + +export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable { + addClass(container, 'file-icon-themable-tree'); + addClass(container, 'show-file-icons'); + + const onDidChangeFileIconTheme = (theme: IFileIconTheme) => { + toggleClass(container, 'align-icons-and-twisties', theme.hasFileIcons && !theme.hasFolderIcons); + toggleClass(container, 'hide-arrows', theme.hidesExplorerArrows === true); + }; + + onDidChangeFileIconTheme(themeService.getFileIconTheme()); + return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme); +} \ No newline at end of file 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 c538216e9fd..625d1deacbf 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -42,6 +42,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; +import { createFileIconThemableTreeContainerScope } from 'vs/workbench/browser/parts/views/views'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; @@ -225,6 +226,8 @@ export class ExplorerView extends ViewletPanel { const filesRenderer = this.instantiationService.createInstance(FilesRenderer, explorerLabels); this.disposables.push(filesRenderer); + this.disposables.push(createFileIconThemableTreeContainerScope(container, this.themeService)); + this.tree = new WorkbenchAsyncDataTree(container, new ExplorerDelegate(), [filesRenderer], this.instantiationService.createInstance(ExplorerDataSource), { accessibilityProvider: new ExplorerAccessibilityProvider(), diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index d1f0a073413..57f7a7a8669 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -196,7 +196,8 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i qualifier = baseThemeClassName + ' ' + qualifier; } - let expanded = '.monaco-tree-row.expanded'; // workaround for #11453 + const expanded = '.monaco-tree-row.expanded'; // workaround for #11453 + const expanded2 = '.monaco-tl-twistie.collapsible:not(.collapsed) + .monaco-tl-contents'; // new tree if (associations.folder) { addSelector(`${qualifier} .folder-icon::before`, associations.folder); @@ -205,6 +206,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i if (associations.folderExpanded) { addSelector(`${qualifier} ${expanded} .folder-icon::before`, associations.folderExpanded); + addSelector(`${qualifier} ${expanded2} .folder-icon::before`, associations.folderExpanded); result.hasFolderIcons = true; } @@ -218,6 +220,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i if (rootFolderExpanded) { addSelector(`${qualifier} ${expanded} .rootfolder-icon::before`, rootFolderExpanded); + addSelector(`${qualifier} ${expanded2} .rootfolder-icon::before`, rootFolderExpanded); result.hasFolderIcons = true; } @@ -237,6 +240,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i if (folderNamesExpanded) { for (let folderName in folderNamesExpanded) { addSelector(`${qualifier} ${expanded} .${escapeCSS(folderName.toLowerCase())}-name-folder-icon.folder-icon::before`, folderNamesExpanded[folderName]); + addSelector(`${qualifier} ${expanded2} .${escapeCSS(folderName.toLowerCase())}-name-folder-icon.folder-icon::before`, folderNamesExpanded[folderName]); result.hasFolderIcons = true; } } From ff9f7cfbfe974eb711d320c38c01af2ab96face4 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 15:57:38 +0100 Subject: [PATCH 0181/1066] minor fix for root labels --- src/vs/workbench/services/label/common/labelService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index d3fd3ea2945..5c550a95d1f 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -102,12 +102,12 @@ export class LabelService implements ILabelService { // Workspace: Single Folder if (isSingleFolderWorkspaceIdentifier(workspace)) { // Folder on disk - const formatter = this.findFormatter(workspace); - const label = options && options.verbose ? this.getUriLabel(workspace) : resourceBasename(workspace); + const label = options && options.verbose ? this.getUriLabel(workspace) : resourceBasename(workspace) || '/'; if (workspace.scheme === Schemas.file) { return label; } + const formatter = this.findFormatter(workspace); const suffix = formatter && formatter.workspace && (typeof formatter.workspace.suffix === 'string') ? formatter.workspace.suffix : workspace.scheme; return suffix ? `${label} (${suffix})` : label; } From fbbc8c59213d75c3895cd81965037b1c7a9c123c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 11 Jan 2019 15:58:23 +0100 Subject: [PATCH 0182/1066] make path.dirname root aware (for #64429) --- src/vs/base/common/paths.ts | 41 ++++++++++++++++------- src/vs/base/common/resources.ts | 3 ++ src/vs/base/test/common/paths.test.ts | 41 +++++++++++++++-------- src/vs/base/test/common/resources.test.ts | 3 ++ 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index d96b7517082..d18519a7731 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -17,6 +17,11 @@ export const sep = '/'; */ export const nativeSep = isWindows ? '\\' : '/'; + +function isPathSeparator(code: number) { + return code === CharCode.Slash || code === CharCode.Backslash; +} + /** * @param path the path to get the dirname from * @param separator the separator to use @@ -24,20 +29,32 @@ export const nativeSep = isWindows ? '\\' : '/'; * */ export function dirname(path: string, separator = nativeSep): string { - const idx = ~path.lastIndexOf('/') || ~path.lastIndexOf('\\'); - if (idx === 0) { + const len = path.length; + if (len === 0) { return '.'; - } else if (~idx === 0) { - return path[0]; - } else if (~idx === path.length - 1) { - return dirname(path.substring(0, path.length - 1)); - } else { - let res = path.substring(0, ~idx); - if (isWindows && res[res.length - 1] === ':') { - res += separator; // make sure drive letters end with backslash - } - return res; + } else if (len === 1) { + return isPathSeparator(path.charCodeAt(0)) ? path : '.'; } + const root = getRoot(path, separator); + let rootLength = root.length; + if (rootLength >= len) { + return root; // matched the root + } + if (rootLength === 0 && isPathSeparator(path.charCodeAt(0))) { + rootLength = 1; // absolute paths stay absolute paths. + } + + let i = len - 1; + if (i > rootLength) { + i--; // no need to look at the last character. If it's a trailing slash, we ignore it. + while (i > rootLength && !isPathSeparator(path.charCodeAt(i))) { + i--; + } + } + if (i === 0) { + return '.'; // it was a relative path with a single segment, no root. Nodejs returns '.' here. + } + return path.substr(0, i); } /** diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index d3170eb406b..a10a3b01e74 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -76,6 +76,9 @@ export function dirname(resource: URI): URI | null { if (resource.scheme === Schemas.file) { return URI.file(paths.dirname(fsPath(resource))); } + if (resource.path.length === 0) { + return resource; + } let dirname = paths.dirname(resource.path, '/'); if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character diff --git a/src/vs/base/test/common/paths.test.ts b/src/vs/base/test/common/paths.test.ts index e0cf182e378..835e7e5d2ef 100644 --- a/src/vs/base/test/common/paths.test.ts +++ b/src/vs/base/test/common/paths.test.ts @@ -8,21 +8,34 @@ import * as platform from 'vs/base/common/platform'; suite('Paths', () => { - test('dirname', () => { - assert.equal(paths.dirname('foo/bar'), 'foo'); - assert.equal(paths.dirname('foo\\bar'), 'foo'); - assert.equal(paths.dirname('/foo/bar'), '/foo'); - assert.equal(paths.dirname('\\foo\\bar'), '\\foo'); - assert.equal(paths.dirname('/foo'), '/'); - assert.equal(paths.dirname('\\foo'), '\\'); - assert.equal(paths.dirname('/'), '/'); - assert.equal(paths.dirname('\\'), '\\'); - assert.equal(paths.dirname('foo'), '.'); - assert.equal(paths.dirname('/folder/'), '/'); - if (platform.isWindows) { - assert.equal(paths.dirname('c:\\some\\file.txt'), 'c:\\some'); - assert.equal(paths.dirname('c:\\some'), 'c:\\'); + function assertDirname(path: string, expected: string, win = false) { + const actual = paths.dirname(path, win ? '\\' : '/'); + + if (actual !== expected) { + assert.fail(`${path}: expected: ${expected}, ours: ${actual}`); } + } + + test('dirname', () => { + assertDirname('foo/bar', 'foo'); + assertDirname('foo\\bar', 'foo', true); + assertDirname('/foo/bar', '/foo'); + assertDirname('\\foo\\bar', '\\foo', true); + assertDirname('/foo', '/'); + assertDirname('\\foo', '\\', true); + assertDirname('/', '/'); + assertDirname('\\', '\\', true); + assertDirname('foo', '.'); + assertDirname('f', '.'); + assertDirname('f/', '.'); + assertDirname('/folder/', '/'); + assertDirname('c:\\some\\file.txt', 'c:\\some', true); + assertDirname('c:\\some', 'c:\\', true); + assertDirname('c:\\', 'c:\\', true); + assertDirname('c:', 'c:', true); + assertDirname('\\\\server\\share\\some\\path', '\\\\server\\share\\some', true); + assertDirname('\\\\server\\share\\some', '\\\\server\\share\\', true); + assertDirname('\\\\server\\share\\', '\\\\server\\share\\', true); }); test('normalize', () => { diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 919855add12..9816adbbbe9 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -47,6 +47,7 @@ suite('Resources', () => { assert.equal(dirname(URI.file('c:\\some\\file\\'))!.toString(), 'file:///c%3A/some'); assert.equal(dirname(URI.file('c:\\some'))!.toString(), 'file:///c%3A/'); assert.equal(dirname(URI.file('C:\\some'))!.toString(), 'file:///c%3A/'); + assert.equal(dirname(URI.file('c:\\'))!.toString(), 'file:///c%3A/'); } else { assert.equal(dirname(URI.file('/some/file/test.txt'))!.toString(), 'file:///some/file'); assert.equal(dirname(URI.file('/some/file/'))!.toString(), 'file:///some'); @@ -56,6 +57,8 @@ suite('Resources', () => { assert.equal(dirname(URI.parse('foo://a/some/file/'))!.toString(), 'foo://a/some'); assert.equal(dirname(URI.parse('foo://a/some/file'))!.toString(), 'foo://a/some'); assert.equal(dirname(URI.parse('foo://a/some'))!.toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a/'))!.toString(), 'foo://a/'); + assert.equal(dirname(URI.parse('foo://a'))!.toString(), 'foo://a'); // does not explode (https://github.com/Microsoft/vscode/issues/41987) dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' })); From ae5078f4433d12cb2a2edb24d6f2705313126f03 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 11 Jan 2019 15:58:43 +0100 Subject: [PATCH 0183/1066] Breadcrumb for "out of workspace" files is bad. Fixes #64429 --- src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts index 1e1de8c7dca..9ef5e5681fa 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsModel.ts @@ -108,13 +108,16 @@ export class EditorBreadcrumbsModel { folder: workspaceService.getWorkspaceFolder(uri), path: [] }; - while (uri.path !== '/') { if (info.folder && isEqual(info.folder.uri, uri)) { break; } info.path.unshift(new FileElement(uri, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER)); + let prevPathLength = uri.path.length; uri = dirname(uri); + if (uri.path.length === prevPathLength) { + break; + } } if (info.folder && workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE) { From 14ce5183db4d016383a779b436c30b434c1f86c0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 11 Jan 2019 16:13:01 +0100 Subject: [PATCH 0184/1066] renames, #58649 --- src/vs/editor/common/modes.ts | 38 ++++++++++++------- .../contrib/goToDefinition/goToDefinition.ts | 16 ++++---- .../goToDefinition/goToDefinitionCommands.ts | 12 +++--- .../goToDefinition/goToDefinitionMouse.ts | 10 ++--- src/vs/monaco.d.ts | 36 +++++++++++------- .../mainThreadLanguageFeatures.ts | 28 +++++++------- src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../api/node/extHostLanguageFeatures.ts | 26 ++++++------- .../api/node/extHostTypeConverters.ts | 6 +-- 9 files changed, 98 insertions(+), 78 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 77c7cdb59ba..b666a0f7e43 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -715,20 +715,32 @@ export interface Location { */ range: IRange; } -/** - * The definition of a symbol represented as one or many [locations](#Location). - * For most programming languages there is only one location at which a symbol is - * defined. - */ -export type Definition = Location | Location[]; -export interface DefinitionLink { - origin?: IRange; +export interface LocationLink { + /** + * A range to select where this link originates from. + */ + originSelectionRange?: IRange; + + /** + * The target uri this link points to. + */ uri: URI; + + /** + * The full range this link points to. + */ range: IRange; - selectionRange?: IRange; + + /** + * A range to select this link points to. Must be contained + * in `LocationLink.range`. + */ + targetSelectionRange?: IRange; } +export type Definition = Location | Location[] | LocationLink[]; + /** * The definition provider interface defines the contract between extensions and * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) @@ -738,7 +750,7 @@ export interface DefinitionProvider { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -750,7 +762,7 @@ export interface DeclarationProvider { /** * Provide the declaration of the symbol at the given position and document. */ - provideDeclaration(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideDeclaration(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -761,7 +773,7 @@ export interface ImplementationProvider { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -772,7 +784,7 @@ export interface TypeDefinitionProvider { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts index 6eb05aa38da..c21bfc60193 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinition.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinition.ts @@ -9,7 +9,7 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; -import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult } from 'vs/editor/common/modes'; +import { LocationLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult } from 'vs/editor/common/modes'; import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry'; @@ -17,12 +17,12 @@ function getDefinitions( model: ITextModel, position: Position, registry: LanguageFeatureRegistry, - provide: (provider: T, model: ITextModel, position: Position) => ProviderResult -): Promise { + provide: (provider: T, model: ITextModel, position: Position) => ProviderResult +): Promise { const provider = registry.ordered(model); // get results - const promises = provider.map((provider): Promise => { + const promises = provider.map((provider): Promise => { return Promise.resolve(provide(provider, model, position)).then(undefined, err => { onUnexpectedExternalError(err); return null; @@ -34,25 +34,25 @@ function getDefinitions( } -export function getDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { +export function getDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position) => { return provider.provideDefinition(model, position, token); }); } -export function getDeclarationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { +export function getDeclarationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { return getDefinitions(model, position, DeclarationProviderRegistry, (provider, model, position) => { return provider.provideDeclaration(model, position, token); }); } -export function getImplementationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { +export function getImplementationsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position) => { return provider.provideImplementation(model, position, token); }); } -export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { +export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position, token: CancellationToken): Promise { return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position) => { return provider.provideTypeDefinition(model, position, token); }); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 5acca6b5149..48cd1ad95d7 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -15,7 +15,7 @@ import * as corePosition from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel, IWordAtPosition } from 'vs/editor/common/model'; -import { DefinitionLink, Location } from 'vs/editor/common/modes'; +import { LocationLink, Location } from 'vs/editor/common/modes'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; @@ -69,7 +69,7 @@ export class DefinitionAction extends EditorAction { // * remove falsy references // * find reference at the current pos let idxOfCurrent = -1; - const result: DefinitionLink[] = []; + const result: LocationLink[] = []; for (const reference of references) { if (!reference || !reference.range) { continue; @@ -113,7 +113,7 @@ export class DefinitionAction extends EditorAction { return definitionPromise; } - protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { return getDefinitionsAtPosition(model, position, token); } @@ -256,7 +256,7 @@ export class PeekDefinitionAction extends DefinitionAction { export class DeclarationAction extends DefinitionAction { - protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { return getDeclarationsAtPosition(model, position, token); } @@ -320,7 +320,7 @@ export class PeekDeclarationAction extends DeclarationAction { } export class ImplementationAction extends DefinitionAction { - protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { return getImplementationsAtPosition(model, position, token); } @@ -378,7 +378,7 @@ export class PeekImplementationAction extends ImplementationAction { } export class TypeDefinitionAction extends DefinitionAction { - protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { + protected _getTargetLocationForPosition(model: ITextModel, position: corePosition.Position, token: CancellationToken): Promise { return getTypeDefinitionsAtPosition(model, position, token); } diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index d33f2b9ac68..55e2e6e0047 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -12,7 +12,7 @@ import { MarkdownString } from 'vs/base/common/htmlContent'; import { IModeService } from 'vs/editor/common/services/modeService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { DefinitionProviderRegistry, DefinitionLink } from 'vs/editor/common/modes'; +import { DefinitionProviderRegistry, LocationLink } from 'vs/editor/common/modes'; import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { getDefinitionsAtPosition } from './goToDefinition'; @@ -35,7 +35,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private toUnhook: IDisposable[]; private decorations: string[]; private currentWordUnderMouse: IWordAtPosition; - private previousPromise: CancelablePromise; + private previousPromise: CancelablePromise; constructor( editor: ICodeEditor, @@ -152,8 +152,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC const previewValue = this.getPreviewValue(textEditorModel, startLineNumber); let wordRange: Range; - if (result.origin) { - wordRange = Range.lift(result.origin); + if (result.originSelectionRange) { + wordRange = Range.lift(result.originSelectionRange); } else { wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); } @@ -281,7 +281,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC DefinitionProviderRegistry.has(this.editor.getModel()); } - private findDefinition(target: IMouseTarget, token: CancellationToken): Promise { + private findDefinition(target: IMouseTarget, token: CancellationToken): Promise { const model = this.editor.getModel(); if (!model) { return Promise.resolve(null); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b484673dbb5..4ec47e159d8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5018,20 +5018,28 @@ declare namespace monaco.languages { range: IRange; } - /** - * The definition of a symbol represented as one or many [locations](#Location). - * For most programming languages there is only one location at which a symbol is - * defined. - */ - export type Definition = Location | Location[]; - - export interface DefinitionLink { - origin?: IRange; + export interface LocationLink { + /** + * A range to select where this link originates from. + */ + originSelectionRange?: IRange; + /** + * The target uri this link points to. + */ uri: Uri; + /** + * The full range this link points to. + */ range: IRange; - selectionRange?: IRange; + /** + * A range to select this link points to. Must be contained + * in `LocationLink.range`. + */ + targetSelectionRange?: IRange; } + export type Definition = Location | Location[] | LocationLink[]; + /** * The definition provider interface defines the contract between extensions and * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) @@ -5041,7 +5049,7 @@ declare namespace monaco.languages { /** * Provide the definition of the symbol at the given position and document. */ - provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -5053,7 +5061,7 @@ declare namespace monaco.languages { /** * Provide the declaration of the symbol at the given position and document. */ - provideDeclaration(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideDeclaration(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -5064,7 +5072,7 @@ declare namespace monaco.languages { /** * Provide the implementation of the symbol at the given position and document. */ - provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideImplementation(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** @@ -5075,7 +5083,7 @@ declare namespace monaco.languages { /** * Provide the type definition of the symbol at the given position and document. */ - provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; + provideTypeDefinition(model: editor.ITextModel, position: Position, token: CancellationToken): ProviderResult; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index 86411c0fc92..eeec73908cf 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -69,17 +69,17 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha } } - private static _reviveDefinitionLinkDto(data: DefinitionLinkDto): modes.DefinitionLink; - private static _reviveDefinitionLinkDto(data: DefinitionLinkDto[]): modes.DefinitionLink[]; - private static _reviveDefinitionLinkDto(data: DefinitionLinkDto | DefinitionLinkDto[]): modes.DefinitionLink | modes.DefinitionLink[] { + private static _reviveLocationLinkDto(data: DefinitionLinkDto): modes.LocationLink; + private static _reviveLocationLinkDto(data: DefinitionLinkDto[]): modes.LocationLink[]; + private static _reviveLocationLinkDto(data: DefinitionLinkDto | DefinitionLinkDto[]): modes.LocationLink | modes.LocationLink[] { if (!data) { - return data; + return data; } else if (Array.isArray(data)) { - data.forEach(l => MainThreadLanguageFeatures._reviveDefinitionLinkDto(l)); - return data; + data.forEach(l => MainThreadLanguageFeatures._reviveLocationLinkDto(l)); + return data; } else { data.uri = URI.revive(data.uri); - return data; + return data; } } @@ -150,8 +150,8 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.DefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { - provideDefinition: (model, position, token): Promise => { - return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); + provideDefinition: (model, position, token): Promise => { + return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } }); } @@ -159,23 +159,23 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha $registerDeclarationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.DeclarationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { provideDeclaration: (model, position, token) => { - return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); + return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } }); } $registerImplementationSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.ImplementationProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { - provideImplementation: (model, position, token): Promise => { - return this._proxy.$provideImplementation(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); + provideImplementation: (model, position, token): Promise => { + return this._proxy.$provideImplementation(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } }); } $registerTypeDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void { this._registrations[handle] = modes.TypeDefinitionProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { - provideTypeDefinition: (model, position, token): Promise => { - return this._proxy.$provideTypeDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveDefinitionLinkDto); + provideTypeDefinition: (model, position, token): Promise => { + return this._proxy.$provideTypeDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } }); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 9bd72240a46..53f6578b4b9 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -804,10 +804,10 @@ export interface LocationDto { } export interface DefinitionLinkDto { - origin?: IRange; + originSelectionRange?: IRange; uri: UriComponents; range: IRange; - selectionRange?: IRange; + targetSelectionRange?: IRange; } export interface WorkspaceSymbolDto extends IdObject { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index de1cd42dbfd..0801f8f05d9 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -143,7 +143,7 @@ class CodeLensAdapter { } } -function convertToDefinitionLinks(value: vscode.Definition): modes.DefinitionLink[] { +function convertToLocationLinks(value: vscode.Definition): modes.LocationLink[] { if (Array.isArray(value)) { return (value as (vscode.DefinitionLink | vscode.Location)[]).map(typeConvert.DefinitionLink.from); } else if (value) { @@ -159,10 +159,10 @@ class DefinitionAdapter { private readonly _provider: vscode.DefinitionProvider ) { } - provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise { + provideDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideDefinition(doc, pos, token)).then(convertToDefinitionLinks); + return asPromise(() => this._provider.provideDefinition(doc, pos, token)).then(convertToLocationLinks); } } @@ -173,10 +173,10 @@ class DeclarationAdapter { private readonly _provider: vscode.DeclarationProvider ) { } - provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Promise { + provideDeclaration(resource: URI, position: IPosition, token: CancellationToken): Promise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideDeclaration(doc, pos, token)).then(convertToDefinitionLinks); + return asPromise(() => this._provider.provideDeclaration(doc, pos, token)).then(convertToLocationLinks); } } @@ -187,10 +187,10 @@ class ImplementationAdapter { private readonly _provider: vscode.ImplementationProvider ) { } - provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Promise { + provideImplementation(resource: URI, position: IPosition, token: CancellationToken): Promise { let doc = this._documents.getDocumentData(resource).document; let pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideImplementation(doc, pos, token)).then(convertToDefinitionLinks); + return asPromise(() => this._provider.provideImplementation(doc, pos, token)).then(convertToLocationLinks); } } @@ -201,10 +201,10 @@ class TypeDefinitionAdapter { private readonly _provider: vscode.TypeDefinitionProvider ) { } - provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise { + provideTypeDefinition(resource: URI, position: IPosition, token: CancellationToken): Promise { const doc = this._documents.getDocumentData(resource).document; const pos = typeConvert.Position.to(position); - return asPromise(() => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToDefinitionLinks); + return asPromise(() => this._provider.provideTypeDefinition(doc, pos, token)).then(convertToLocationLinks); } } @@ -1060,7 +1060,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + $provideDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { return this._withAdapter(handle, DefinitionAdapter, adapter => adapter.provideDefinition(URI.revive(resource), position, token)); } @@ -1070,7 +1070,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + $provideDeclaration(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { return this._withAdapter(handle, DeclarationAdapter, adapter => adapter.provideDeclaration(URI.revive(resource), position, token)); } @@ -1080,7 +1080,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + $provideImplementation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { return this._withAdapter(handle, ImplementationAdapter, adapter => adapter.provideImplementation(URI.revive(resource), position, token)); } @@ -1090,7 +1090,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return this._createDisposable(handle); } - $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { + $provideTypeDefinition(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise { return this._withAdapter(handle, TypeDefinitionAdapter, adapter => adapter.provideTypeDefinition(URI.revive(resource), position, token)); } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index d1a3c878936..81e79899972 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -583,16 +583,16 @@ export namespace location { } export namespace DefinitionLink { - export function from(value: vscode.Location | vscode.DefinitionLink): modes.DefinitionLink { + export function from(value: vscode.Location | vscode.DefinitionLink): modes.LocationLink { const definitionLink = value; const location = value; return { - origin: definitionLink.originSelectionRange + originSelectionRange: definitionLink.originSelectionRange ? Range.from(definitionLink.originSelectionRange) : undefined, uri: definitionLink.targetUri ? definitionLink.targetUri : location.uri, range: Range.from(definitionLink.targetRange ? definitionLink.targetRange : location.range), - selectionRange: definitionLink.targetSelectionRange + targetSelectionRange: definitionLink.targetSelectionRange ? Range.from(definitionLink.targetSelectionRange) : undefined, }; From bf245d196b97b025cbcae896d83d91b068fd3f1d Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 16:33:19 +0100 Subject: [PATCH 0185/1066] move properly on file events --- src/vs/workbench/parts/files/electron-browser/explorerService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index e459bba9f83..d51ea23ddb8 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -192,6 +192,7 @@ export class ExplorerService implements IExplorerService { // Move in Model modelElements.forEach((modelElement, index) => { const oldParent = modelElement.parent; + modelElement.move(newParents[index]); this._onDidChangeItem.fire(oldParent); this._onDidChangeItem.fire(newParents[index]); }); From bb21186a5cc3006d7f92b0c5f46a8fe1d75ef78a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 11 Jan 2019 16:41:10 +0100 Subject: [PATCH 0186/1066] tree: clear auto expand disposable on drop --- src/vs/base/browser/ui/tree/abstractTree.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index ecd6078b7cd..65c8b83ef84 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -104,6 +104,9 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } drop(data: IDragAndDropData, targetNode: ITreeNode | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { + this.autoExpandDisposable.dispose(); + this.autoExpandNode = undefined; + this.dnd.drop(asTreeDragAndDropData(data), targetNode && targetNode.element, targetIndex, originalEvent); } } From 7a207910cf826c8f45f2b4eb3c787384348dadde Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 11 Jan 2019 17:09:09 +0100 Subject: [PATCH 0187/1066] Fix #66401 --- .../browser/parts/views/customView.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index d3494ac9b65..89b94f45a22 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -451,14 +451,28 @@ export class CustomTreeView extends Disposable implements ITreeView { refresh(elements?: ITreeItem[]): Promise { if (this.dataProvider && this.tree) { - elements = elements || [this.root]; + if (!elements) { + elements = [this.root]; + // remove all waiting elements to refresh if root is asked to refresh + this.elementsToRefresh = []; + } for (const element of elements) { element.children = null; // reset children } if (this.isVisible) { return this.doRefresh(elements); } else { - this.elementsToRefresh.push(...elements); + if (this.elementsToRefresh.length) { + const seen: Set = new Set(); + this.elementsToRefresh.forEach(element => seen.add(element.handle)); + for (const element of elements) { + if (!seen.has(element.handle)) { + this.elementsToRefresh.push(element); + } + } + } else { + this.elementsToRefresh.push(...elements); + } } } return Promise.resolve(undefined); From 3763ab7b0a47d76ae453a4d4c0076a38e727deff Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 11 Jan 2019 17:13:48 +0100 Subject: [PATCH 0188/1066] async data tree: clear cached refresh promises on setinput fixes #65500 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 40 ++++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index a3ec69680f8..4bbcc410b6d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -9,12 +9,13 @@ import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOve import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { timeout, always } from 'vs/base/common/async'; +import { timeout, always, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { toggleClass } from 'vs/base/browser/dom'; import { Iterator } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { isPromiseCanceledError } from 'vs/base/common/errors'; enum AsyncDataTreeNodeState { Uninitialized, @@ -221,7 +222,7 @@ export class AsyncDataTree implements IDisposable private readonly tree: ObjectTree, TFilterData>; private readonly root: IAsyncDataTreeNode; private readonly nodes = new Map>(); - private readonly refreshPromises = new Map, Promise>(); + private readonly refreshPromises = new Map, CancelablePromise>(); private readonly identityProvider?: IIdentityProvider; private readonly _onDidChangeNodeState = new Emitter>(); @@ -327,6 +328,9 @@ export class AsyncDataTree implements IDisposable } setInput(input: TInput): Promise { + this.refreshPromises.forEach(promise => promise.cancel()); + this.refreshPromises.clear(); + this.root.element = input!; return this.refresh(input); } @@ -486,25 +490,13 @@ export class AsyncDataTree implements IDisposable } private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { - await this._refreshNode(node, recursive, reason); + await this.doRefresh(node, recursive, reason); if (recursive && node.children) { await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason))); } } - private _refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { - let result = this.refreshPromises.get(node); - - if (result) { - return result; - } - - result = this.doRefresh(node, recursive, reason); - this.refreshPromises.set(node, result); - return always(result, () => this.refreshPromises.delete(node)); - } - private doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); @@ -524,7 +516,7 @@ export class AsyncDataTree implements IDisposable this._onDidChangeNodeState.fire(node); }, _ => null); - return Promise.resolve(this.dataSource.getChildren(node.element!)) + return Promise.resolve(this.doGetChildren(node)) .then(children => { slowTimeout.cancel(); node.state = AsyncDataTreeNodeState.Loaded; @@ -533,6 +525,10 @@ export class AsyncDataTree implements IDisposable this.setChildren(node, children, recursive); this._onDidResolveChildren.fire({ element: node.element as T, reason }); }, err => { + if (isPromiseCanceledError(err)) { + return Promise.resolve(null); + } + slowTimeout.cancel(); node.state = AsyncDataTreeNodeState.Uninitialized; this._onDidChangeNodeState.fire(node); @@ -546,6 +542,18 @@ export class AsyncDataTree implements IDisposable } } + private doGetChildren(node: IAsyncDataTreeNode): Promise { + let result = this.refreshPromises.get(node); + + if (result) { + return result; + } + + result = createCancelablePromise(_ => Promise.resolve(this.dataSource.getChildren(node.element!))); + this.refreshPromises.set(node, result); + return always(result, () => this.refreshPromises.delete(node)); + } + private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { if (!node.collapsed && node.element.state === AsyncDataTreeNodeState.Uninitialized) { if (deep) { From 3eac8a2cd764b592deaf083d230b58cef7651b1c Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 11 Jan 2019 17:16:25 +0100 Subject: [PATCH 0189/1066] use target selection range #58649 --- src/vs/editor/common/modes.ts | 7 +++++ .../goToDefinition/goToDefinitionCommands.ts | 31 +++++++++++-------- .../referenceSearch/referencesModel.ts | 6 ++-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index b666a0f7e43..7908774ac43 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -739,6 +739,13 @@ export interface LocationLink { targetSelectionRange?: IRange; } +export function isLocationLink(thing: any): thing is LocationLink { + return thing + && URI.isUri((thing as LocationLink).uri) + && Range.isIRange((thing as LocationLink).range) + && (Range.isIRange((thing as LocationLink).originSelectionRange) || Range.isIRange((thing as LocationLink).targetSelectionRange)); +} + export type Definition = Location | Location[] | LocationLink[]; /** diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 48cd1ad95d7..47af90c355f 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -12,10 +12,10 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, IActionOptions, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as corePosition from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel, IWordAtPosition } from 'vs/editor/common/model'; -import { LocationLink, Location } from 'vs/editor/common/modes'; +import { LocationLink, Location, isLocationLink } from 'vs/editor/common/modes'; import { MessageController } from 'vs/editor/contrib/message/messageController'; import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget'; import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController'; @@ -29,7 +29,6 @@ import { IProgressService } from 'vs/platform/progress/common/progress'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition } from './goToDefinition'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; - export class DefinitionActionConfig { constructor( @@ -44,7 +43,7 @@ export class DefinitionActionConfig { export class DefinitionAction extends EditorAction { - private _configuration: DefinitionActionConfig; + private readonly _configuration: DefinitionActionConfig; constructor(configuration: DefinitionActionConfig, opts: IActionOptions) { super(opts); @@ -74,14 +73,10 @@ export class DefinitionAction extends EditorAction { if (!reference || !reference.range) { continue; } - let { uri, range } = reference; - let newLen = result.push({ - uri, - range - }); + const newLen = result.push(reference); if (this._configuration.filterCurrent - && uri.toString() === model.uri.toString() - && Range.containsPosition(range, pos) + && reference.uri.toString() === model.uri.toString() + && Range.containsPosition(reference.range, pos) && idxOfCurrent === -1 ) { idxOfCurrent = newLen - 1; @@ -145,11 +140,21 @@ export class DefinitionAction extends EditorAction { } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location, sideBySide: boolean): Promise { + private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise { + // range is the target-selection-range when we have one + // and the the fallback is the 'full' range + let range: IRange = undefined; + if (isLocationLink(reference)) { + range = reference.targetSelectionRange; + } + if (!range) { + range = reference.range; + } + return editorService.openCodeEditor({ resource: reference.uri, options: { - selection: Range.collapseToStart(reference.range), + selection: Range.collapseToStart(range), revealIfOpened: true, revealInCenterIfOutsideViewport: true } diff --git a/src/vs/editor/contrib/referenceSearch/referencesModel.ts b/src/vs/editor/contrib/referenceSearch/referencesModel.ts index 6bf6c110b1e..da7677a01d5 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesModel.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesModel.ts @@ -11,7 +11,7 @@ import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { defaultGenerator } from 'vs/base/common/idGenerator'; import { Range, IRange } from 'vs/editor/common/core/range'; -import { Location } from 'vs/editor/common/modes'; +import { Location, LocationLink } from 'vs/editor/common/modes'; import { ITextModelService, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { Position } from 'vs/editor/common/core/position'; @@ -170,7 +170,7 @@ export class ReferencesModel implements IDisposable { readonly _onDidChangeReferenceRange = new Emitter(); readonly onDidChangeReferenceRange: Event = this._onDidChangeReferenceRange.event; - constructor(references: Location[]) { + constructor(references: LocationLink[]) { this._disposables = []; // grouping and sorting references.sort(ReferencesModel._compareReferences); @@ -187,7 +187,7 @@ export class ReferencesModel implements IDisposable { if (current.children.length === 0 || !Range.equalsRange(ref.range, current.children[current.children.length - 1].range)) { - let oneRef = new OneReference(current, ref.range); + let oneRef = new OneReference(current, ref.targetSelectionRange || ref.range); this._disposables.push(oneRef.onRefChanged((e) => this._onDidChangeReferenceRange.fire(e))); this.references.push(oneRef); current.children.push(oneRef); From 9031a4f3c72f671d342b8afea88edd5abd00501f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 11 Jan 2019 17:32:39 +0100 Subject: [PATCH 0190/1066] monaco.d.ts pain --- src/vs/monaco.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 4ec47e159d8..6dd1279bb4a 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5038,6 +5038,8 @@ declare namespace monaco.languages { targetSelectionRange?: IRange; } + export function isLocationLink(thing: any): thing is LocationLink; + export type Definition = Location | Location[] | LocationLink[]; /** From 0e5f4af14dad267b7a698ca74e828e73e5457047 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 11 Jan 2019 17:39:55 +0100 Subject: [PATCH 0191/1066] #66397 Use `galleryIdentifier` instead of `identfier` in `ILocalExtension` --- src/vs/code/node/cliProcessMain.ts | 8 ++-- .../common/extensionManagement.ts | 2 +- .../common/extensionManagementUtil.ts | 6 +-- .../node/extensionManagementIpc.ts | 8 ++-- .../node/extensionManagementService.ts | 47 +++++++++---------- .../localizations/node/localizations.ts | 6 +-- .../electron-browser/extensionTipsService.ts | 4 +- .../electron-browser/extensionsUtils.ts | 8 ++-- .../electron-browser/extensionsViewlet.ts | 6 +-- .../electron-browser/extensionsViews.ts | 4 +- .../node/extensionsWorkbenchService.ts | 36 +++++++------- .../electron-browser/preferencesSearch.ts | 4 +- .../electron-browser/extensionService.ts | 4 +- .../electron-browser/workbenchIssueService.ts | 2 +- 14 files changed, 68 insertions(+), 77 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index ed0972812b4..fd186de133c 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -35,7 +35,7 @@ import { StateService } from 'vs/platform/state/node/stateService'; import { createSpdLogService } from 'vs/platform/log/node/spdlogService'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { isPromiseCanceledError } from 'vs/base/common/errors'; -import { areSameExtensions, getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { URI } from 'vs/base/common/uri'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; @@ -153,7 +153,7 @@ class Main { return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`)); } - const [installedExtension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id })); + const [installedExtension] = installed.filter(e => areSameExtensions(e.galleryIdentifier, { id })); if (installedExtension) { if (extension.version !== installedExtension.manifest.version) { if (version || force) { @@ -186,7 +186,7 @@ class Main { const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User); - const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0]; + const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.galleryIdentifier) && semver.gt(local.manifest.version, manifest.version))[0]; if (newer && !force) { console.log(localize('forceDowngrade', "A newer version of this extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", newer.galleryIdentifier.id, newer.manifest.version, manifest.version)); @@ -225,7 +225,7 @@ class Main { return sequence(extensions.map(extension => () => { return getExtensionId(extension).then(id => { return this.extensionManagementService.getInstalled(ExtensionType.User).then(installed => { - const [extension] = installed.filter(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(e) }, { id })); + const [extension] = installed.filter(e => areSameExtensions(e.galleryIdentifier, { id })); if (!extension) { return Promise.reject(new Error(`${notInstalled(id)}\n${useId}`)); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 0f02bc86468..3d84049a84c 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -91,7 +91,7 @@ export interface IGalleryMetadata { } export interface ILocalExtension extends IExtension { - manifest: IExtensionManifest; /** TODO:@sandy081 - remove the readonly relaxment */ + readonly manifest: IExtensionManifest; metadata: IGalleryMetadata; readmeUrl: URI | null; changelogUrl: URI | null; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index a766977aa2f..238ebbe3f9b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -25,10 +25,6 @@ export function getGalleryExtensionId(publisher: string, name: string): string { return `${publisher.toLocaleLowerCase()}.${name.toLocaleLowerCase()}`; } -export function getGalleryExtensionIdFromLocal(local: ILocalExtension): string { - return local.manifest ? getGalleryExtensionId(local.manifest.publisher, local.manifest.name) : local.identifier.id; -} - export const LOCAL_EXTENSION_ID_REGEX = /^([^.]+\..+)-(\d+\.\d+\.\d+(-.*)?)$/; export function getIdFromLocalExtensionId(localExtensionId: string): string { @@ -74,7 +70,7 @@ export function groupByExtension(extensions: T[], getExtensionIdentifier: (t: export function getLocalExtensionTelemetryData(extension: ILocalExtension): any { return { - id: getGalleryExtensionIdFromLocal(extension), + id: extension.galleryIdentifier.id, name: extension.manifest.name, galleryId: null, publisherId: extension.metadata ? extension.metadata.publisherId : null, diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index a816fe8f8ab..56e64302099 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -21,11 +21,9 @@ function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): UR function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { transformer = transformer ? transformer : DefaultURITransformer; - const manfiest = extension.manifest; - delete extension.manifest; - extension = transformAndReviveIncomingURIs(extension, transformer); - extension.manifest = manfiest; - return extension; + const manifest = extension.manifest; + const transformed = transformAndReviveIncomingURIs({ ...extension, ...{ manifest: undefined } }, transformer); + return { ...transformed, ...{ manifest } }; } function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 19b7a603188..dd98e91377f 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -21,7 +21,7 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionIdFromLocal, adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } 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'; @@ -150,7 +150,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } zip(extension: ILocalExtension): Promise { - this.logService.trace('ExtensionManagementService#zip', extension.identifier.id); + this.logService.trace('ExtensionManagementService#zip', extension.galleryIdentifier.id); return this.collectFiles(extension) .then(files => zip(path.join(tmpdir(), generateUuid()), files)) .then(path => URI.file(path)); @@ -204,7 +204,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; return this.getInstalled(ExtensionType.User) .then(installedExtensions => { - const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, { id: getGalleryExtensionIdFromLocal(local) }) && semver.gt(local.manifest.version, manifest.version))[0]; + const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.galleryIdentifier) && semver.gt(local.manifest.version, manifest.version))[0]; return newer ? this.uninstall(newer, true) : undefined; }) .then(() => { @@ -349,7 +349,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } reinstallFromGallery(extension: ILocalExtension): Promise { - this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id); + this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.galleryIdentifier.id); if (!this.galleryService.isEnabled()) { return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); } @@ -367,7 +367,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private getOperation(extensionToInstall: IExtensionIdentifier, installed: ILocalExtension[]): InstallOperation { - return installed.some(i => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i), uuid: i.identifier.uuid }, extensionToInstall)) ? InstallOperation.Update : InstallOperation.Install; + return installed.some(i => areSameExtensions(i.galleryIdentifier, extensionToInstall)) ? InstallOperation.Update : InstallOperation.Install; } private getTelemetryEvent(operation: InstallOperation): string { @@ -530,7 +530,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } uninstall(extension: ILocalExtension, force = false): Promise { - this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); + this.logService.trace('ExtensionManagementService#uninstall', extension.galleryIdentifier.id); return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User) .then(installed => { const extensionsToUninstall = installed @@ -545,7 +545,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { - this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); + this.logService.trace('ExtensionManagementService#updateMetadata', local.galleryIdentifier.id); local.metadata = metadata; return this.saveMetadataForLocalExtension(local) .then(localExtension => { @@ -572,11 +572,11 @@ export class ExtensionManagementService extends Disposable implements IExtension } private findGalleryExtension(local: ILocalExtension): Promise { - if (local.identifier.uuid) { - return this.findGalleryExtensionById(local.identifier.uuid) - .then(galleryExtension => galleryExtension ? galleryExtension : this.findGalleryExtensionByName(getGalleryExtensionIdFromLocal(local))); + if (local.galleryIdentifier.uuid) { + return this.findGalleryExtensionById(local.galleryIdentifier.uuid) + .then(galleryExtension => galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.galleryIdentifier.id)); } - return this.findGalleryExtensionByName(getGalleryExtensionIdFromLocal(local)); + return this.findGalleryExtensionByName(local.galleryIdentifier.id); } private findGalleryExtensionById(uuid: string): Promise { @@ -672,31 +672,30 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.resolve(pfs.exists(extension.location.fsPath)) .then(exists => exists ? null : Promise.reject(new Error(nls.localize('notExists', "Could not find extension")))) .then(() => { - this.logService.info('Uninstalling extension:', extension.identifier.id); + this.logService.info('Uninstalling extension:', extension.galleryIdentifier.id); this._onUninstallExtension.fire(extension.identifier); }); } private uninstallExtension(local: ILocalExtension): Promise { - const id = getGalleryExtensionIdFromLocal(local); - let promise = this.uninstallingExtensions.get(id); + let promise = this.uninstallingExtensions.get(local.galleryIdentifier.id); if (!promise) { // Set all versions of the extension as uninstalled promise = createCancelablePromise(token => this.scanUserExtensions(false) - .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions({ id: getGalleryExtensionIdFromLocal(u), uuid: u.identifier.uuid }, { id, uuid: local.identifier.uuid })))) - .then(() => { this.uninstallingExtensions.delete(id); })); - this.uninstallingExtensions.set(id, promise); + .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.galleryIdentifier, local.galleryIdentifier)))) + .then(() => { this.uninstallingExtensions.delete(local.galleryIdentifier.id); })); + this.uninstallingExtensions.set(local.galleryIdentifier.id, promise); } return promise; } private async postUninstallExtension(extension: ILocalExtension, error?: Error): Promise { if (error) { - this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message); + this.logService.error('Failed to uninstall extension:', extension.galleryIdentifier.id, error.message); } else { - this.logService.info('Successfully uninstalled extension:', extension.identifier.id); + this.logService.info('Successfully uninstalled extension:', extension.galleryIdentifier.id); // only report if extension has a mapped gallery extension. UUID identifies the gallery extension. - if (extension.identifier.uuid) { + if (extension.galleryIdentifier.uuid) { await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); } } @@ -753,7 +752,7 @@ export class ExtensionManagementService extends Disposable implements IExtension .then(([uninstalled, extensions]) => { extensions = extensions.filter(e => !uninstalled[e.identifier.id]); if (excludeOutdated) { - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid })); + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.galleryIdentifier); extensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]); } this.logService.info('Scanned user extensions:', extensions.length); @@ -808,7 +807,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const toRemove: ILocalExtension[] = []; // Outdated extensions - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => ({ id: getGalleryExtensionIdFromLocal(e), uuid: e.identifier.uuid })); + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.galleryIdentifier); toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); return Promise.all(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); @@ -822,8 +821,8 @@ export class ExtensionManagementService extends Disposable implements IExtension } private removeExtension(extension: ILocalExtension, type: string): Promise { - this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id); - return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id)); + this.logService.trace(`Deleting ${type} extension from disk`, extension.galleryIdentifier.id, extension.location.fsPath); + return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.galleryIdentifier.id, extension.location.fsPath)); } private isUninstalled(id: string): Promise { diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index a74772a0216..0eb9361d7db 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -9,7 +9,7 @@ import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } fr import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Queue } from 'vs/base/common/async'; -import { areSameExtensions, getGalleryExtensionIdFromLocal, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; import product from 'vs/platform/node/product'; @@ -68,7 +68,7 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe private onDidInstallExtension(extension: ILocalExtension | undefined): void { if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) { - this.logService.debug('Adding language packs from the extension', extension.identifier.id); + this.logService.debug('Adding language packs from the extension', extension.galleryIdentifier.id); this.update(); } } @@ -136,7 +136,7 @@ class LanguagePacksCache extends Disposable { } private createLanguagePacksFromExtension(languagePacks: { [language: string]: ILanguagePack }, extension: ILocalExtension): void { - const extensionIdentifier = { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid }; + const extensionIdentifier = extension.galleryIdentifier; const localizations = extension.manifest.contributes && extension.manifest.contributes.localizations ? extension.manifest.contributes.localizations : []; for (const localizationContribution of localizations) { if (extension.location.scheme === Schemas.file && isValidLocalization(localizationContribution)) { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 6b17eadcff6..afa61cbed1d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -40,7 +40,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { Emitter, Event } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; -import { areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/parts/experiments/node/experimentService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { getKeywordsForExtension } from 'vs/workbench/parts/extensions/electron-browser/extensionsUtils'; @@ -421,7 +421,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } this.extensionsService.getInstalled(ExtensionType.User).then(local => { - const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, { id: getGalleryExtensionIdFromLocal(local) }))); + const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, local.galleryIdentifier))); if (!recommendations.length) { return Promise.resolve(undefined); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index 2f0dfa6915e..6fe7ca63d57 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -13,7 +13,7 @@ import { IExtensionManagementService, ILocalExtension, IExtensionEnablementServi import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { areSameExtensions, adoptToGalleryExtensionId, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import product from 'vs/platform/node/product'; @@ -126,9 +126,9 @@ export function getInstalledExtensions(accessor: ServicesAccessor): Promise { return extensions.map(extension => { return { - identifier: { id: adoptToGalleryExtensionId(stripVersion(extension.identifier.id)), uuid: extension.identifier.uuid }, + identifier: extension.galleryIdentifier, local: extension, - globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.identifier)) + globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.galleryIdentifier)) }; }); }); @@ -137,7 +137,7 @@ export function getInstalledExtensions(accessor: ServicesAccessor): Promise areSameExtensions({ id: extensionId }, { id: getGalleryExtensionIdFromLocal(extension.local) })); + return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(({ extensionId }) => areSameExtensions({ id: extensionId }, extension.local.galleryIdentifier)); } function stripVersion(id: string): string { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 5ee6810db7f..18cf1514789 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -40,7 +40,7 @@ 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'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { getGalleryExtensionIdFromLocal, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -635,13 +635,13 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { return this.extensionsManagementService.getInstalled(ExtensionType.User).then(installed => { const maliciousExtensions = installed - .filter(e => maliciousSet.has(getGalleryExtensionIdFromLocal(e))); + .filter(e => maliciousSet.has(e.galleryIdentifier.id)); if (maliciousExtensions.length) { return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => { this.notificationService.prompt( Severity.Warning, - localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", getGalleryExtensionIdFromLocal(e)), + localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.galleryIdentifier.id), [{ label: localize('reloadNow', "Reload Now"), run: () => this.windowService.reloadWindow() diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index d22fc4f1555..ee4d08b28bb 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -232,7 +232,7 @@ export class ExtensionsListView extends ViewletPanel { && e.local.manifest.contributes && Array.isArray(e.local.manifest.contributes.grammars) && e.local.manifest.contributes.grammars.length - && e.local.identifier.id !== 'git'; + && e.local.galleryIdentifier.id !== 'vscode.git'; }); return this.getPagedModel(this.sortExtensions(basics, options)); } @@ -240,7 +240,7 @@ export class ExtensionsListView extends ViewletPanel { const others = result.filter(e => { return e.local.manifest && e.local.manifest.contributes - && (!Array.isArray(e.local.manifest.contributes.grammars) || e.local.identifier.id === 'git') + && (!Array.isArray(e.local.manifest.contributes.grammars) || e.local.galleryIdentifier.id === 'vscode.git') && !Array.isArray(e.local.manifest.contributes.themes); }); return this.getPagedModel(this.sortExtensions(others, options)); diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 50685c9f6fd..94b58d437d6 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -16,7 +16,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionIdFromLocal, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -78,11 +78,11 @@ class Extension implements IExtension { if (this.gallery) { return this.gallery.identifier; } - return { id: getGalleryExtensionIdFromLocal(this.local), uuid: this.local.identifier.uuid }; + return this.local.galleryIdentifier; } get uuid(): string | undefined { - return this.gallery ? this.gallery.identifier.uuid : this.local.identifier.uuid; + return this.gallery ? this.gallery.identifier.uuid : this.local.galleryIdentifier.uuid; } get publisher(): string { @@ -432,13 +432,13 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.extensionService.getInstalled() .then(installed => this.getDistinctInstalledExtensions(installed) .then(distinctInstalled => { - const installedById = index(this.installed, e => e.local.identifier.id); - const groupById = groupBy(installed, i => getGalleryExtensionIdFromLocal(i)); + const installedById = index(this.installed, e => e.identifier.id); + const groupById = groupBy(installed, i => i.galleryIdentifier.id); this.installed = distinctInstalled.map(local => { - const locals = groupById[getGalleryExtensionIdFromLocal(local)]; + const locals = groupById[local.galleryIdentifier.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); + const extension = installedById[local.galleryIdentifier.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; @@ -498,7 +498,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, 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, { id: getGalleryExtensionIdFromLocal(extension), uuid: extension.identifier.uuid })); + const isDisabled = disabledExtensionIdentifiers.some(identifier => areSameExtensions(identifier, extension.galleryIdentifier)); if (isDisabled) { return extension.location.scheme === Schemas.file ? 'disabled:primary' : 'disabled:secondary'; } else { @@ -511,21 +511,20 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, for (const extension of (groups['enabled'] || [])) { if (runtimeExtensions.some(r => r.extensionLocation.toString() === extension.location.toString())) { enabled.push(extension); - seenExtensions[getGalleryExtensionIdFromLocal(extension)] = true; + seenExtensions[extension.galleryIdentifier.id] = true; } else { notRunningExtensions.push(extension); } } for (const extension of notRunningExtensions) { - if (!seenExtensions[getGalleryExtensionIdFromLocal(extension)]) { + if (!seenExtensions[extension.galleryIdentifier.id]) { enabled.push(extension); - seenExtensions[getGalleryExtensionIdFromLocal(extension)] = true; + seenExtensions[extension.galleryIdentifier.id] = true; } } const primaryDisabled = groups['disabled:primary'] || []; const secondaryDisabled = (groups['disabled:secondary'] || []).filter(disabled => { - const identifier: IExtensionIdentifier = { id: getGalleryExtensionIdFromLocal(disabled), uuid: disabled.identifier.uuid }; - return primaryDisabled.every(p => !areSameExtensions({ id: getGalleryExtensionIdFromLocal(p), uuid: p.identifier.uuid }, identifier)); + return primaryDisabled.every(p => !areSameExtensions(p.galleryIdentifier, disabled.galleryIdentifier)); }); return [...enabled, ...primaryDisabled, ...secondaryDisabled]; }); @@ -534,11 +533,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private hasDuplicates(extensions: ILocalExtension[]): boolean { const seen: { [key: string]: boolean; } = Object.create(null); for (const i of extensions) { - const key = getGalleryExtensionIdFromLocal(i); - if (seen[key]) { + if (seen[i.galleryIdentifier.id]) { return true; } - seen[key] = true; + seen[i.galleryIdentifier.id] = true; } return false; } @@ -711,7 +709,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, title: nls.localize('uninstallingExtension', 'Uninstalling extension....'), - source: `${toUninstall[0].identifier.id}` + source: `${toUninstall[0].galleryIdentifier.id}` }, () => Promise.all(toUninstall.map(local => this.extensionService.uninstall(local))).then(() => undefined)); } @@ -754,7 +752,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, - source: `${toReinstall[0].identifier.id}` + source: `${toReinstall[0].galleryIdentifier.id}` }, () => Promise.all(toReinstall.map(local => this.extensionService.reinstallFromGallery(local))).then(() => undefined)); } @@ -767,7 +765,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } private checkAndEnableDisabledDependencies(extensionIdentifier: IExtensionIdentifier): Promise { - const extension = this.local.filter(e => (e.local || e.gallery) && areSameExtensions(extensionIdentifier, e.local ? e.local.identifier : e.gallery!.identifier))[0]; + const extension = this.local.filter(e => (e.local || e.gallery) && areSameExtensions(extensionIdentifier, e.identifier))[0]; if (extension) { const disabledDepencies = this.getExtensionsRecursively([extension], this.local, EnablementState.Enabled, { dependencies: true, pack: false }); if (disabledDepencies.length) { diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index fc8c467e942..ab549ec5d9e 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -49,7 +49,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences return exts .filter(ext => this.extensionEnablementService.isEnabled(ext)) .filter(ext => ext.manifest && ext.manifest.contributes && ext.manifest.contributes.configuration) - .filter(ext => !!ext.identifier.uuid); + .filter(ext => !!ext.galleryIdentifier.uuid); }); } @@ -393,7 +393,7 @@ class RemoteSearchProvider implements ISearchProvider { } private getExtensionFilter(ext: ILocalExtension): string { - const uuid = ext.identifier.uuid; + const uuid = ext.galleryIdentifier.uuid; const versionString = ext.manifest.version .split('.') .map(versionPart => strings.pad(versionPart, 10)) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 0d31a2088c5..c30fbead131 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -14,7 +14,7 @@ import { isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { BetterMergeId, areSameExtensions, getGalleryExtensionIdFromLocal } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import pkg from 'vs/platform/node/package'; @@ -494,7 +494,7 @@ export class ExtensionService extends Disposable implements IExtensionService { if (extensionsToDisable.length) { return this._extensionManagementService.getInstalled(ExtensionType.User) .then(installed => { - const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions({ id: getGalleryExtensionIdFromLocal(i) }, e))); + const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions(i.galleryIdentifier, e))); return Promise.all(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); }) .then(() => { diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index 80c4ac2f824..d4cfd888c38 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -40,7 +40,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { repositoryUrl: manifest.repository && manifest.repository.url, bugsUrl: manifest.bugs && manifest.bugs.url, displayName: manifest.displayName, - id: extension.identifier.id, + id: extension.galleryIdentifier.id, isTheme: isTheme }; }); From af15970f91020fed05de570e1f441de35437fd10 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 11 Jan 2019 17:44:43 +0100 Subject: [PATCH 0192/1066] :lipstick: --- .../extensionManagement/node/extensionManagementService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index dd98e91377f..3e5ea6037e8 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -261,7 +261,7 @@ export class ExtensionManagementService extends Disposable implements IExtension async installFromGallery(extension: IGalleryExtension): Promise { const startTime = new Date().getTime(); - this.logService.info('Installing extension:', extension.name); + this.logService.info('Installing extension:', extension.identifier.id); this._onInstallExtension.fire({ identifier: extension.identifier, gallery: extension }); const onDidInstallExtensionSuccess = (extension: IGalleryExtension, operation: InstallOperation, local: ILocalExtension) => { @@ -386,11 +386,11 @@ export class ExtensionManagementService extends Disposable implements IExtension publisherDisplayName: extension.publisherDisplayName, }; - this.logService.trace('Started downloading extension:', extension.name); + this.logService.trace('Started downloading extension:', extension.identifier.id); return this.galleryService.download(extension, operation) .then( zipPath => { - this.logService.info('Downloaded extension:', extension.name, zipPath); + this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); return getManifest(zipPath) .then( manifest => ({ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }), From a71334aedd129e6dc2a924db7cc070d3b4ecd2cf Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 11 Jan 2019 17:58:19 +0100 Subject: [PATCH 0193/1066] explicit refresh should drop cash --- src/vs/workbench/parts/files/common/files.ts | 1 + .../parts/files/electron-browser/explorerService.ts | 5 +++++ .../parts/files/electron-browser/fileActions.ts | 12 +++++------- .../files/electron-browser/views/explorerView.ts | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index f80f5166a8b..f7c25973e45 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -49,6 +49,7 @@ export interface IExplorerService { setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; findClosest(resource: URI): ExplorerItem | null; + refresh(): void; /** * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index d51ea23ddb8..3e6c9da424c 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -134,6 +134,11 @@ export class ExplorerService implements IExplorerService { }); } + refresh(): void { + this.model.roots.forEach(r => r.isDirectoryResolved = false); + this._onDidChangeItem.fire(null); + } + // File events private onFileOperation(e: FileOperationEvent): void { diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index db6b2c76bcf..95c5873db29 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -853,18 +853,16 @@ export class RefreshExplorerView extends Action { constructor( id: string, label: string, - @IViewletService private readonly viewletService: IViewletService + @IViewletService private readonly viewletService: IViewletService, + @IExplorerService private readonly explorerService: IExplorerService ) { super(id, label, 'explorer-action refresh-explorer'); } public run(): Promise { - return this.viewletService.openViewlet(VIEWLET_ID, true).then((viewlet: ExplorerViewlet) => { - const explorerView = viewlet.getExplorerView(); - if (explorerView) { - explorerView.refresh(); - } - }); + return this.viewletService.openViewlet(VIEWLET_ID, true).then(() => + this.explorerService.refresh() + ); } } 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 625d1deacbf..d56dc3b72db 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -355,7 +355,7 @@ export class ExplorerView extends ViewletPanel { * Refresh the contents of the explorer to get up to date data from the disk about the file structure. * If the item is passed we refresh only that level of the tree, otherwise we do a full refresh. */ - refresh(item?: ExplorerItem): Promise { + private refresh(item?: ExplorerItem): Promise { if (!this.tree || !this.isBodyVisible()) { this.shouldRefresh = true; return Promise.resolve(undefined); From 611d5c8340757cf2853d58b4e1bb8899c4a4b9bc Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Fri, 11 Jan 2019 09:31:58 -0800 Subject: [PATCH 0194/1066] Eliminating unnecessary span elements from LinkDetector results --- .../parts/debug/browser/linkDetector.ts | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/linkDetector.ts b/src/vs/workbench/parts/debug/browser/linkDetector.ts index 4bfd2b72671..606b54bb5b9 100644 --- a/src/vs/workbench/parts/debug/browser/linkDetector.ts +++ b/src/vs/workbench/parts/debug/browser/linkDetector.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import strings = require('vs/base/common/strings'); import { isAbsolute } from 'vs/base/common/paths'; import { URI as uri } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; @@ -32,7 +33,7 @@ export class LinkDetector { * Matches and handles absolute file links in the string provided. * Returns element that wraps the processed string, where matched links are replaced by and unmatched parts are surrounded by elements. * 'onclick' event is attached to all anchored links that opens them in the editor. - * If no links were detected, returns the original string wrapped in a . + * Each line of the text, even if it contains no links, is wrapped in a and added as a child of the returned . */ handleLinks(text: string): HTMLElement { const container = document.createElement('span'); @@ -40,7 +41,7 @@ export class LinkDetector { // Handle the text one line at a time const lines = text.split('\n'); - if (text.charAt(text.length - 1) === '\n') { + if (strings.endsWith(text, '\n')) { // Remove the last element ('') that split added lines.pop(); } @@ -49,7 +50,7 @@ export class LinkDetector { let line = lines[i]; // Re-introduce the newline for every line except the last (unless the last line originally ended with a newline) - if (i < lines.length - 1 || text.charAt(text.length - 1) === '\n') { + if (i < lines.length - 1 || strings.endsWith(text, '\n')) { line += '\n'; } @@ -106,20 +107,30 @@ export class LinkDetector { } } - // If we found any matches for this pattern, don't check any more patterns + // If we found any matches for this pattern, don't check any more patterns. Other parts of the line will be checked for the other patterns due to the recursion. if (lineContainer.hasChildNodes()) { break; } } - if (!lineContainer.hasChildNodes()) { - // We didn't find a match for any of the patterns. Add the unmodified line. - let span = document.createElement('span'); - span.textContent = line; - lineContainer.appendChild(span); + if (lines.length === 1) { + if (lineContainer.hasChildNodes()) { + // Adding lineContainer to container would introduce an unnecessary surrounding span since there is only one line, so instead we just return lineContainer + return lineContainer; + } else { + container.textContent = line; + } + } else { + if (lineContainer.hasChildNodes()) { + // Add this line to the container + container.appendChild(lineContainer); + } else { + // No links were added, but we still need to surround the unmodified line with a span before adding it + let span = document.createElement('span'); + span.textContent = line; + container.appendChild(span); + } } - - container.appendChild(lineContainer); } return container; From 97844047f2895599ff8d1bfc7c6a1bc399da3353 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Fri, 11 Jan 2019 09:32:25 -0800 Subject: [PATCH 0195/1066] LinkDetector tests --- .../debug/test/browser/linkDetector.test.ts | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts diff --git a/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts new file mode 100644 index 00000000000..782956c5020 --- /dev/null +++ b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; +import { LinkDetector } from 'vs/workbench/parts/debug/browser/linkDetector'; +import { isWindows } from 'vs/base/common/platform'; + +suite('Debug - Link Detector', () => { + + let linkDetector: LinkDetector; + + /** + * Instantiate a {@link LinkDetector} for use by the functions being tested. + */ + setup(() => { + const instantiationService: TestInstantiationService = workbenchInstantiationService(); + linkDetector = instantiationService.createInstance(LinkDetector); + }); + + /** + * Assert that a given Element is an anchor element with an onClick event. + * + * @param element The Element to verify. + */ + function assertElementIsLink(element: Element) { + assert(element instanceof HTMLAnchorElement); + assert.notEqual(null, (element as HTMLAnchorElement).onclick); + } + + test('noLinks', () => { + const input = 'I am a string'; + const expectedOutput = 'I am a string'; + const output = linkDetector.handleLinks(input); + + assert.equal(0, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal(expectedOutput, output.outerHTML); + }); + + test('trailingNewline', () => { + const input = 'I am a string\n'; + const expectedOutput = 'I am a string\n'; + const output = linkDetector.handleLinks(input); + + assert.equal(0, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal(expectedOutput, output.outerHTML); + }); + + test('singleLineLink', () => { + const input = isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34'; + const expectedOutput = /^.*\/foo\/bar.js:12:34<\/a><\/span>$/; + const output = linkDetector.handleLinks(input); + + assert.equal(1, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('A', output.firstElementChild.tagName); + assert(expectedOutput.test(output.outerHTML)); + assertElementIsLink(output.firstElementChild); + assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.firstElementChild.textContent); + }); + + test('relativeLink', () => { + const input = '\./foo/bar.js'; + const expectedOutput = '\./foo/bar.js'; + const output = linkDetector.handleLinks(input); + + assert.equal(0, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal(expectedOutput, output.outerHTML); + }); + + test('singleLineLinkAndText', function () { + var input = isWindows ? 'The link: C:/foo/bar.js:12:34' : 'The link: /Users/foo/bar.js:12:34'; + var expectedOutput = /^The link: <\/span>.*\/foo\/bar.js:12:34<\/a><\/span>$/; + var output = linkDetector.handleLinks(input); + + assert.equal(2, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('SPAN', output.children[0].tagName); + assert.equal('A', output.children[1].tagName); + assert(expectedOutput.test(output.outerHTML)); + assertElementIsLink(output.children[1]); + assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].textContent); + }); + + test('singleLineMultipleLinks', () => { + const input = isWindows ? 'Here is a link C:/foo/bar.js:12:34 and here is another D:/boo/far.js:56:78' : + 'Here is a link /Users/foo/bar.js:12:34 and here is another /Users/boo/far.js:56:78'; + const expectedOutput = /^Here is a link <\/span>.*\/foo\/bar.js:12:34<\/a> and here is another <\/span>.*\/boo\/far.js:56:78<\/a><\/span>$/; + const output = linkDetector.handleLinks(input); + + assert.equal(4, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('SPAN', output.children[0].tagName); + assert.equal('A', output.children[1].tagName); + assert.equal('SPAN', output.children[2].tagName); + assert.equal('A', output.children[3].tagName); + assert(expectedOutput.test(output.outerHTML)); + assertElementIsLink(output.children[1]); + assertElementIsLink(output.children[3]); + assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].textContent); + assert.equal(isWindows ? 'C:/boo/far.js:56:78' : '/Users/boo/far.js:56:78', output.children[3].textContent); + }); + + test('multilineNoLinks', () => { + const input = 'Line one\nLine two\nLine three'; + const expectedOutput = /^Line one\n<\/span>Line two\n<\/span>Line three<\/span><\/span>$/; + const output = linkDetector.handleLinks(input); + + assert.equal(3, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('SPAN', output.children[0].tagName); + assert.equal('SPAN', output.children[1].tagName); + assert.equal('SPAN', output.children[2].tagName); + assert(expectedOutput.test(output.outerHTML)); + }); + + test('multilineTrailingNewline', () => { + const input = 'I am a string\nAnd I am another\n'; + const expectedOutput = 'I am a string\n<\/span>And I am another\n<\/span><\/span>'; + const output = linkDetector.handleLinks(input); + + assert.equal(2, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('SPAN', output.children[0].tagName); + assert.equal('SPAN', output.children[1].tagName); + assert.equal(expectedOutput, output.outerHTML); + }); + + test('multilineWithLinks', () => { + const input = isWindows ? 'I have a link for you\nHere it is: C:/foo/bar.js:12:34\nCool, huh?' : + 'I have a link for you\nHere it is: /Users/foo/bar.js:12:34\nCool, huh?'; + const expectedOutput = /^I have a link for you\n<\/span>Here it is: <\/span>.*\/foo\/bar.js:12:34<\/a>\n<\/span><\/span>Cool, huh\?<\/span><\/span>$/; + const output = linkDetector.handleLinks(input); + + assert.equal(3, output.children.length); + assert.equal('SPAN', output.tagName); + assert.equal('SPAN', output.children[0].tagName); + assert.equal('SPAN', output.children[1].tagName); + assert.equal('SPAN', output.children[2].tagName); + assert.equal('SPAN', output.children[1].children[0].tagName); + assert.equal('A', output.children[1].children[1].tagName); + assert.equal('SPAN', output.children[1].children[2].tagName); + assert(expectedOutput.test(output.outerHTML)); + assertElementIsLink(output.children[1].children[1]); + assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].children[1].textContent); + }); +}); From e1d8493f28cb46c0753d039eca07d5de764f2e65 Mon Sep 17 00:00:00 2001 From: Gabriel DeBacker Date: Fri, 11 Jan 2019 10:27:18 -0800 Subject: [PATCH 0196/1066] Implement creating a razzle terminal with approved API changes --- src/vs/vscode.d.ts | 10 +++++ .../mainThreadTerminalService.ts | 5 ++- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- .../api/node/extHostTerminalService.ts | 7 ++-- .../parts/terminal/common/terminal.ts | 8 ++++ .../terminalProcessManager.ts | 40 +++++++++++-------- 6 files changed, 50 insertions(+), 22 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index f896c56d5af..6067c70dd38 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6843,6 +6843,16 @@ declare module 'vscode' { * Object with environment variables that will be added to the VS Code process. */ env?: { [key: string]: string | null }; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.windows.env` on top. + * When this is true, the complete environment must be provided as nothing will be inherited from the process + * or any configuration. + */ + strictEnv?: boolean; } /** diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 5a3efa811e7..49bee8d7d2d 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -55,7 +55,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape // when the extension host process goes down ? } - public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean): Promise<{ id: number, name: string }> { + public $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }> { const shellLaunchConfig: IShellLaunchConfig = { name, executable: shellPath, @@ -63,7 +63,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape cwd, waitOnExit, ignoreConfigurationCwd: true, - env + env, + strictEnv }; const terminal = this.terminalService.createTerminal(shellLaunchConfig); return Promise.resolve({ diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 53f6578b4b9..b1120075a6c 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -344,7 +344,7 @@ export interface MainThreadProgressShape extends IDisposable { } export interface MainThreadTerminalServiceShape extends IDisposable { - $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean): Promise<{ id: number, name: string }>; + $createTerminal(name?: string, shellPath?: string, shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, waitOnExit?: boolean, strictEnv?: boolean): Promise<{ id: number, name: string }>; $createTerminalRenderer(name: string): Promise; $dispose(terminalId: number): void; $hide(terminalId: number): void; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 41193102a03..96059529056 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -104,9 +104,10 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi shellArgs?: string[], cwd?: string | URI, env?: { [key: string]: string }, - waitOnExit?: boolean + waitOnExit?: boolean, + strictEnv?: boolean ): void { - this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit).then(terminal => { + this._proxy.$createTerminal(this._name, shellPath, shellArgs, cwd, env, waitOnExit, strictEnv).then(terminal => { this._name = terminal.name; this._runQueuedRequests(terminal.id); }); @@ -269,7 +270,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { public createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal { const terminal = new ExtHostTerminal(this._proxy, options.name); - terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env /*, options.waitOnExit*/); + terminal.create(options.shellPath, options.shellArgs, options.cwd, options.env, /*options.waitOnExit*/ undefined, options.strictEnv); this._terminals.push(terminal); return terminal; } diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 8ba0bea8dde..91c9b53fd80 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -181,6 +181,14 @@ export interface IShellLaunchConfig { * extensions full control over the terminal. */ isRendererOnly?: boolean; + + /** + * Whether the terminal process environment should be exactly as provided in + * `TerminalOptions.env`. When this is false (default), the environment will be based on the + * window's environment and also apply configured platform settings like + * `terminal.integrated.windows.env` on top. + */ + strictEnv?: boolean; } export interface ITerminalService { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index a4fdb4ed501..a0f4af3ac7d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -111,26 +111,34 @@ export class TerminalProcessManager implements ITerminalProcessManager { const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); - // Resolve env vars from config and shell - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; - const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); - const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); - const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); - shellLaunchConfig.env = envFromShell; - // Compell type system as process.env should not have any undefined entries - const env: platform.IProcessEnvironment = { ...process.env } as any; + let env: platform.IProcessEnvironment = {}; - // Merge process env with the env from config and from shellLaunchConfig - terminalEnvironment.mergeEnvironments(env, envFromConfig); - terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + // When this is true, the caller must provide the complete environment as nothing will be inherited from the process + // or any configuration. + if (shellLaunchConfig.strictEnv) { + env = { ...shellLaunchConfig.env } as any; + } else { + // Merge process env with the env from config and from shellLaunchConfig + env = { ...process.env } as any; - // Sanitize the environment, removing any undesirable VS Code and Electron environment - // variables - terminalEnvironment.sanitizeEnvironment(env); + // Resolve env vars from config and shell + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; + const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); + const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); + const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); + shellLaunchConfig.env = envFromShell; - // Adding other env keys necessary to create the process - terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, this._configHelper.config.setLocaleVariables); + terminalEnvironment.mergeEnvironments(env, envFromConfig); + terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + + // Sanitize the environment, removing any undesirable VS Code and Electron environment + // variables + terminalEnvironment.sanitizeEnvironment(env); + + // Adding other env keys necessary to create the process + 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); From 0635b47a4caaddfcfeaea9575c2d19d1a8c10897 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 11 Jan 2019 10:38:50 -0800 Subject: [PATCH 0197/1066] Fix condition --- src/vs/workbench/parts/terminal/node/terminalEnvironment.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts index 729951d8eaa..2175f1beb12 100644 --- a/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts +++ b/src/vs/workbench/parts/terminal/node/terminalEnvironment.ts @@ -61,7 +61,7 @@ export function sanitizeEnvironment(env: ITerminalEnvironment): void { const envKeys = Object.keys(env); envKeys.forEach(envKey => { for (let i = 0; i < keysToRemove.length; i++) { - if (envKey.search(keysToRemove[i])) { + if (envKey.search(keysToRemove[i]) !== -1) { delete env[envKey]; break; } From 66511a0bdb3dcfb40bfdae74108d9da93cd9b6f4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 15:16:45 -0800 Subject: [PATCH 0198/1066] Interup geterror during rename Rename is a user triggered operation and should interupt long run error computations --- .../typescript-language-features/src/features/rename.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index 7b1c0ac791e..7112cb91867 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -36,8 +36,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { if (this.client.apiVersion.gte(API.v310)) { const triggerSpan = renameInfo.triggerSpan; if (triggerSpan) { - const range = typeConverters.Range.fromTextSpan(triggerSpan); - return range; + return typeConverters.Range.fromTextSpan(triggerSpan); } } @@ -90,7 +89,9 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { findInComments: false }; - return this.client.execute('rename', args, token); + return this.client.interuptGetErr(() => { + return this.client.execute('rename', args, token); + }); } private updateLocs( From d2a8ca946fc51899d461993fc23338c97ab45639 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 15:51:12 -0800 Subject: [PATCH 0199/1066] Use isCancellationRequested --- .../typescript-language-features/src/tsServer/server.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index e3c1dbff0af..eb4be3aaf24 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -308,18 +308,16 @@ export class TypeScriptServer extends Disposable { }; let result: Promise; if (executeInfo.expectsResult) { - let wasCancelled = false; 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) { executeInfo.token.onCancellationRequested(() => { - wasCancelled = true; this.tryCancelRequest(request.seq, command); }); } }).catch((err: any) => { - if (!wasCancelled) { + if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { this._logger.error(`'${command}' request failed with error.`, err); const properties = this.parseErrorText(err && err.message, command); /* __GDPR__ From 3da39a64dd6d12fa479a59bab8913a5d32c8a195 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 16:08:37 -0800 Subject: [PATCH 0200/1066] Improve logging of TypeScript server errors Fixes #66366 Fixes #66367 Avoid printing the error twice in the trace Make sure we show helpful error information in the VS Code dev tools when a TS server error occurrs --- .../src/tsServer/callbackMap.ts | 2 +- .../src/tsServer/server.ts | 47 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/callbackMap.ts b/extensions/typescript-language-features/src/tsServer/callbackMap.ts index 5c187ec1485..897a587b924 100644 --- a/extensions/typescript-language-features/src/tsServer/callbackMap.ts +++ b/extensions/typescript-language-features/src/tsServer/callbackMap.ts @@ -8,7 +8,7 @@ import { CancelledResponse, ServerResponse } from '../typescriptService'; export interface CallbackItem { readonly onSuccess: (value: R) => void; - readonly onError: (err: any) => void; + readonly onError: (err: Error) => void; readonly startTime: number; readonly isAsync: boolean; } diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index eb4be3aaf24..2007fbf0916 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -24,6 +24,14 @@ import { Reader } from '../utils/wireProtocol'; import { CallbackMap } from './callbackMap'; import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; +class TypeScriptServerError extends Error { + constructor( + public readonly response: Proto.Response, + ) { + super(response.message); + } +} + export class TypeScriptServerSpawner { public constructor( private readonly _versionProvider: TypeScriptVersionProvider, @@ -55,7 +63,7 @@ export class TypeScriptServerSpawner { const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions()); this._logger.info('Started TSServer'); - return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, this._logger, this._telemetryReporter, this._tracer); + return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, this._telemetryReporter, this._tracer); } private getForkOptions() { @@ -176,7 +184,6 @@ export class TypeScriptServer extends Disposable { private readonly _childProcess: cp.ChildProcess, private readonly _tsServerLogFile: string | undefined, private readonly _cancellationPipeName: string | undefined, - private readonly _logger: Logger, private readonly _telemetryReporter: TelemetryReporter, private readonly _tracer: Tracer, ) { @@ -294,7 +301,7 @@ export class TypeScriptServer extends Disposable { // Special case where response itself is successful but there is not any data to return. callback.onSuccess(new NoContentResponse()); } else { - callback.onError(response); + callback.onError(new TypeScriptServerError(response)); } } @@ -316,23 +323,25 @@ export class TypeScriptServer extends Disposable { this.tryCancelRequest(request.seq, command); }); } - }).catch((err: any) => { - if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { - this._logger.error(`'${command}' request failed with error.`, err); - const properties = this.parseErrorText(err && err.message, command); - /* __GDPR__ - "languageServiceErrorResponse" : { - "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this._telemetryReporter.logTelemetry('languageServiceErrorResponse', properties); + }).catch((err: Error) => { + if (err instanceof TypeScriptServerError) { + if (!executeInfo.token || !executeInfo.token.isCancellationRequested) { + const properties = this.parseErrorText(err.response.message, err.response.command); + /* __GDPR__ + "languageServiceErrorResponse" : { + "command" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "stack" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "errortext" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this._telemetryReporter.logTelemetry('languageServiceErrorResponse', properties); + } } + throw err; }); } else { From 60f232b246f2699d4895315de9e03d1747a1f6bd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 16:28:18 -0800 Subject: [PATCH 0201/1066] Include TypeScript version in errors logged to dev tools Fixes #66368 --- .../src/tsServer/server.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 2007fbf0916..64674ce0b46 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -25,10 +25,12 @@ import { CallbackMap } from './callbackMap'; import { RequestItem, RequestQueue, RequestQueueingType } from './requestQueue'; class TypeScriptServerError extends Error { + constructor( + version: TypeScriptVersion, public readonly response: Proto.Response, ) { - super(response.message); + super(`TypeScript Server Error (${version.versionString})\n${response.message}`); } } @@ -49,7 +51,7 @@ export class TypeScriptServerSpawner { ): TypeScriptServer { const apiVersion = version.version || API.defaultVersion; - const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, pluginManager); + const { args, cancellationPipeName, tsServerLogFile } = this.getTsServerArgs(configuration, version, apiVersion, pluginManager); if (TypeScriptServerSpawner.isLoggingEnabled(apiVersion, configuration)) { if (tsServerLogFile) { @@ -63,7 +65,7 @@ export class TypeScriptServerSpawner { const childProcess = electron.fork(version.tsServerPath, args, this.getForkOptions()); this._logger.info('Started TSServer'); - return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, this._telemetryReporter, this._tracer); + return new TypeScriptServer(childProcess, tsServerLogFile, cancellationPipeName, version, this._telemetryReporter, this._tracer); } private getForkOptions() { @@ -77,14 +79,13 @@ export class TypeScriptServerSpawner { private getTsServerArgs( configuration: TypeScriptServiceConfiguration, currentVersion: TypeScriptVersion, + apiVersion: API, pluginManager: PluginManager, ): { args: string[], cancellationPipeName: string | undefined, tsServerLogFile: string | undefined } { const args: string[] = []; let cancellationPipeName: string | undefined; let tsServerLogFile: string | undefined; - const apiVersion = currentVersion.version || API.defaultVersion; - if (apiVersion.gte(API.v206)) { if (apiVersion.gte(API.v250)) { args.push('--useInferredProjectPerProjectRoot'); @@ -184,6 +185,7 @@ export class TypeScriptServer extends Disposable { private readonly _childProcess: cp.ChildProcess, private readonly _tsServerLogFile: string | undefined, private readonly _cancellationPipeName: string | undefined, + private readonly _version: TypeScriptVersion, private readonly _telemetryReporter: TelemetryReporter, private readonly _tracer: Tracer, ) { @@ -301,7 +303,7 @@ export class TypeScriptServer extends Disposable { // Special case where response itself is successful but there is not any data to return. callback.onSuccess(new NoContentResponse()); } else { - callback.onError(new TypeScriptServerError(response)); + callback.onError(new TypeScriptServerError(this._version, response)); } } From 2f5d88899d9575d165aad0a3b6aa6514cdccd696 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 10 Jan 2019 16:34:20 -0800 Subject: [PATCH 0202/1066] Make NoContentResponse an constant instead of a class --- .../typescript-language-features/src/tsServer/server.ts | 2 +- .../typescript-language-features/src/typescriptService.ts | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 64674ce0b46..db7810ad96f 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -301,7 +301,7 @@ 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(new NoContentResponse()); + callback.onSuccess(NoContentResponse); } else { callback.onError(new TypeScriptServerError(this._version, response)); } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 874cb020a94..db791db8fc1 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -19,11 +19,9 @@ export class CancelledResponse { ) { } } -export class NoContentResponse { - public readonly type: 'noContent' = 'noContent'; -} +export const NoContentResponse = new class { readonly type = 'noContent'; }; -export type ServerResponse = T | CancelledResponse | NoContentResponse; +export type ServerResponse = T | CancelledResponse | typeof NoContentResponse; interface TypeScriptRequestTypes { 'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]; From 00dd098dc6d4f8cfc8f2efe359cd63487cf7378d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 11:11:52 -0800 Subject: [PATCH 0203/1066] Mark isLocationLinkFixed as internal --- src/vs/editor/common/modes.ts | 3 +++ src/vs/monaco.d.ts | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 7908774ac43..55581b24c79 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -739,6 +739,9 @@ export interface LocationLink { targetSelectionRange?: IRange; } +/** + * @internal + */ export function isLocationLink(thing: any): thing is LocationLink { return thing && URI.isUri((thing as LocationLink).uri) diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 6dd1279bb4a..4ec47e159d8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5038,8 +5038,6 @@ declare namespace monaco.languages { targetSelectionRange?: IRange; } - export function isLocationLink(thing: any): thing is LocationLink; - export type Definition = Location | Location[] | LocationLink[]; /** From 67973726b678f1363e9ad82ac2ea7e4c14fcbd1e Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 11 Jan 2019 11:20:47 -0800 Subject: [PATCH 0204/1066] Upgrade css service version --- extensions/css-language-features/server/package.json | 2 +- extensions/css-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 28f60c966d7..70a53550e66 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.3", + "vscode-css-languageservice": "^3.0.13-next.4", "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 30c2fb6c62f..d8f8904133f 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.3: - version "3.0.13-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.3.tgz#b9e6f253cace52fbb749d2fe194ae4b196f3543e" - integrity sha512-7+7JddZRt8zFRLbqygxJw+GHtbQ5o2YvgEFvi4ixvbUAX6KlY3Yw6CgUUbg9dmBla7h+GbtoDjwiLFV6Oy+SBQ== +vscode-css-languageservice@^3.0.13-next.4: + version "3.0.13-next.4" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.4.tgz#3ff1a0fefe7b47c698fd48baf05dcea2b709b99d" + integrity sha512-Ji7YpGyGkUZF3KSz1da3dVuKkdGujeDrHE73gRfGxDxb2yE00ifPFueRf1DMXVBSQ4rCeVWzC4gxEhnnLJEc2w== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 73a6a2f46588590403a0092220b21818996333cf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 11 Jan 2019 11:42:52 -0800 Subject: [PATCH 0205/1066] Comment improvements --- src/vs/vscode.d.ts | 5 ++--- src/vs/workbench/parts/terminal/common/terminal.ts | 3 ++- .../terminal/electron-browser/terminalProcessManager.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 6067c70dd38..42d9d87e05e 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -6848,9 +6848,8 @@ declare module 'vscode' { * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the * window's environment and also apply configured platform settings like - * `terminal.integrated.windows.env` on top. - * When this is true, the complete environment must be provided as nothing will be inherited from the process - * or any configuration. + * `terminal.integrated.windows.env` on top. When this is true, the complete environment + * must be provided as nothing will be inherited from the process or any configuration. */ strictEnv?: boolean; } diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 91c9b53fd80..28ff29643b2 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -186,7 +186,8 @@ export interface IShellLaunchConfig { * Whether the terminal process environment should be exactly as provided in * `TerminalOptions.env`. When this is false (default), the environment will be based on the * window's environment and also apply configured platform settings like - * `terminal.integrated.windows.env` on top. + * `terminal.integrated.windows.env` on top. When this is true, the complete environment must be + * provided as nothing will be inherited from the process or any configuration. */ strictEnv?: boolean; } diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index a0f4af3ac7d..ffab899d101 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -111,12 +111,12 @@ export class TerminalProcessManager implements ITerminalProcessManager { const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); this.initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, activeWorkspaceRootUri, this._configHelper.config.cwd); - // Compell type system as process.env should not have any undefined entries + // Compel type system as process.env should not have any undefined entries let env: platform.IProcessEnvironment = {}; - // When this is true, the caller must provide the complete environment as nothing will be inherited from the process - // or any configuration. if (shellLaunchConfig.strictEnv) { + // Only base the terminal process environment on this environment and add the + // various mixins when strictEnv is false env = { ...shellLaunchConfig.env } as any; } else { // Merge process env with the env from config and from shellLaunchConfig From 2b0cee0eba93eabfafdcb3839622e9ded77ca81c Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 11 Jan 2019 12:20:24 -0800 Subject: [PATCH 0206/1066] contributes.css.customData for #66414 --- .../client/src/cssMain.ts | 7 ++++-- .../client/src/customData.ts | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/extensions/css-language-features/client/src/cssMain.ts b/extensions/css-language-features/client/src/cssMain.ts index 7520e61b209..ee8c9973d3a 100644 --- a/extensions/css-language-features/client/src/cssMain.ts +++ b/extensions/css-language-features/client/src/cssMain.ts @@ -11,7 +11,7 @@ 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 { getCustomDataPathsInAllWorkspaces } from './customData'; +import { getCustomDataPathsInAllWorkspaces, getCustomDataPathsFromAllExtensions } from './customData'; // this method is called when vs code is activated export function activate(context: ExtensionContext) { @@ -31,7 +31,10 @@ export function activate(context: ExtensionContext) { let documentSelector = ['css', 'scss', 'less']; - let dataPaths = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); + let dataPaths = [ + ...getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders), + ...getCustomDataPathsFromAllExtensions() + ]; // Options to control the language client let clientOptions: LanguageClientOptions = { diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts index 25cc95ccc92..c9954de8c51 100644 --- a/extensions/css-language-features/client/src/customData.ts +++ b/extensions/css-language-features/client/src/customData.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { workspace, WorkspaceFolder } from 'vscode'; +import { workspace, WorkspaceFolder, extensions } from 'vscode'; interface ExperimentalConfig { experimental?: { - customData?: string[] + customData?: string[]; }; } @@ -28,7 +28,6 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol wfCSSConfig.workspaceFolderValue.experimental && wfCSSConfig.workspaceFolderValue.experimental.customData ) { - wfCSSConfig.workspaceFolderValue.experimental.customData.forEach(p => [ dataPaths.push(path.resolve(wf.uri.fsPath, p)) ]); @@ -37,3 +36,20 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol 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.css && contributes.css.customData && Array.isArray(contributes.css.customData)) { + const relativePaths: string[] = contributes.css.customData; + relativePaths.forEach(rp => { + dataPaths.push(path.resolve(extension.extensionPath, rp)); + }); + } + } + + return dataPaths; +} From e5a596be29b519067b594ffc477f964cf7dca67d Mon Sep 17 00:00:00 2001 From: rebornix Date: Fri, 11 Jan 2019 13:10:54 -0800 Subject: [PATCH 0207/1066] add git blame api --- extensions/git/src/api/api1.ts | 4 ++++ extensions/git/src/api/git.d.ts | 1 + extensions/git/src/git.ts | 13 +++++++++++++ extensions/git/src/repository.ts | 7 ++++++- 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 22b2a87aef1..b4dba92db69 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -179,6 +179,10 @@ export class ApiRepository implements Repository { push(remoteName?: string, branchName?: string, setUpstream: boolean = false): Promise { return this._repository.pushTo(remoteName, branchName, setUpstream); } + + blame(path: string): Promise { + return this._repository.blame(path); + } } export class ApiGit implements Git { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 1a67f8e23f5..5943ebe4b96 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -156,6 +156,7 @@ export interface Repository { fetch(remote?: string, ref?: string, depth?: number): Promise; pull(unshallow?: boolean): Promise; push(remoteName?: string, branchName?: string, setUpstream?: boolean): Promise; + blame(path: string): Promise; } export interface API { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 26f5ae804d8..4a7cf7b8fe8 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1282,6 +1282,19 @@ export class Repository { } } + async blame(path: string): Promise { + try { + const args = ['blame']; + args.push(path); + + let result = await this.run(args); + + return result.stdout.trim(); + } catch (err) { + throw err; + } + } + async createStash(message?: string, includeUntracked?: boolean): Promise { try { const args = ['stash', 'save']; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 1fb29e03f67..8c32162f3f1 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -299,7 +299,8 @@ export const enum Operation { GetObjectDetails = 'GetObjectDetails', SubmoduleUpdate = 'SubmoduleUpdate', RebaseContinue = 'RebaseContinue', - Apply = 'Apply' + Apply = 'Apply', + Blame = 'Blame' } function isReadOnly(operation: Operation): boolean { @@ -999,6 +1000,10 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this.repository.push(remote, undefined, false, true, forcePushMode)); } + async blame(path: string): Promise { + return await this.run(Operation.Blame, () => this.repository.blame(path)); + } + @throttle sync(head: Branch): Promise { return this._sync(head, false); From 6682006c8bbfe5a5fb3a55d4244e98f0597d030c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 11:35:51 -0800 Subject: [PATCH 0208/1066] Use more explicit state for TS server states Try to prevent the tracked server state from getting into weird invalid states and make the state more explicit --- .../src/typescriptServiceClient.ts | 140 ++++++++++-------- 1 file changed, 82 insertions(+), 58 deletions(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 9e02bbedc81..864d028f6fe 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -34,6 +34,42 @@ export interface TsDiagnostics { readonly diagnostics: Proto.Diagnostic[]; } +namespace ServerState { + export const enum Type { + None, + Running, + Errored + } + + export const None = new class { readonly type = Type.None; }; + + export class Running { + readonly type = Type.Running; + constructor( + public readonly server: TypeScriptServer, + + /** + * API version obtained from the version picker after checking the corresponding path exists. + */ + public readonly apiVersion: API, + + /** + * Version reported by currently-running tsserver. + */ + public tsserverVersion: string | undefined, + ) { } + } + + export class Errored { + readonly type = Type.Errored; + constructor( + public readonly error: Error, + ) { } + } + + export type State = typeof None | Running | Errored; +} + export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient { private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`; @@ -49,23 +85,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType public readonly logger: Logger = new Logger(); private readonly typescriptServerSpawner: TypeScriptServerSpawner; - private forkedTsServer: TypeScriptServer | null; - private lastError: Error | null; + private serverState: ServerState.State = ServerState.None; private lastStart: number; private numberRestarts: number; private isRestarting: boolean = false; private loadingIndicator = new ServerInitializingIndicator(); public readonly telemetryReporter: TelemetryReporter; - /** - * API version obtained from the version picker after checking the corresponding path exists. - */ - private _apiVersion: API; - - /** - * Version reported by currently-running tsserver. - */ - private _tsserverVersion: string | undefined; public readonly bufferSyncSupport: BufferSyncSupport; public readonly diagnosticsManager: DiagnosticsManager; @@ -87,8 +113,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType }); this._onReady!.promise = p; - this.forkedTsServer = null; - this.lastError = null; this.numberRestarts = 0; this._configuration = TypeScriptServiceConfiguration.loadFromWorkspace(); @@ -96,8 +120,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.pluginPathsProvider = new TypeScriptPluginPathsProvider(this._configuration); this.versionPicker = new TypeScriptVersionPicker(this.versionProvider, this.workspaceState); - this._apiVersion = API.defaultVersion; - this._tsserverVersion = undefined; this.tracer = new Tracer(this.logger); this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds); @@ -116,7 +138,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.pluginPathsProvider.updateConfiguration(this._configuration); this.tracer.updateConfiguration(); - if (this.forkedTsServer) { + if (this.serverState.type === ServerState.Type.Running) { if (this._configuration.checkJs !== oldConfiguration.checkJs || this._configuration.experimentalDecorators !== oldConfiguration.experimentalDecorators ) { @@ -129,7 +151,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType } }, this, this._disposables); - this.telemetryReporter = this._register(new TelemetryReporter(() => this._tsserverVersion || this._apiVersion.versionString)); + this.telemetryReporter = this._register(new TelemetryReporter(() => { + if (this.serverState.type === ServerState.Type.Running) { + if (this.serverState.tsserverVersion) { + return this.serverState.tsserverVersion; + } + } + return this.apiVersion.versionString; + })); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer); @@ -147,22 +176,21 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.bufferSyncSupport.dispose(); - if (this.forkedTsServer) { - this.forkedTsServer.kill(); + if (this.serverState.type === ServerState.Type.Running) { + this.serverState.server.kill(); } this.loadingIndicator.reset(); } public restartTsServer(): void { - if (this.forkedTsServer) { + if (this.serverState.type === ServerState.Type.Running) { this.info('Killing TS Server'); this.isRestarting = true; - this.forkedTsServer.kill(); - this.resetClientVersion(); + this.serverState.server.kill(); } - this.forkedTsServer = this.startService(true); + this.serverState = this.startService(true); } private readonly _onTsServerStarted = this._register(new vscode.EventEmitter()); @@ -193,7 +221,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType public readonly onSurveyReady = this._onSurveyReady.event; public get apiVersion(): API { - return this._apiVersion; + if (this.serverState.type === ServerState.Type.Running) { + return this.serverState.apiVersion; + } + return API.defaultVersion; } public onReady(f: () => void): Promise { @@ -212,30 +243,30 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.telemetryReporter.logTelemetry(eventName, properties); } - private service(): TypeScriptServer | null { - if (this.forkedTsServer) { - return this.forkedTsServer; + private service(): TypeScriptServer { + if (this.serverState.type === ServerState.Type.Running) { + return this.serverState.server; } - if (this.lastError) { - throw this.lastError; + if (this.serverState.type === ServerState.Type.Errored) { + throw this.serverState.error; } - this.startService(); - if (this.forkedTsServer) { - return this.forkedTsServer; + const newState = this.startService(); + if (newState.type === ServerState.Type.Running) { + return newState.server; } throw new Error('Could not create TS service'); } public ensureServiceStarted() { - if (!this.forkedTsServer) { + if (this.serverState.type !== ServerState.Type.Running) { this.startService(); } } private token: number = 0; - private startService(resendModels: boolean = false): TypeScriptServer | null { + private startService(resendModels: boolean = false): ServerState.State { if (this.isDisposed) { - return null; + return ServerState.None; } let currentVersion = this.versionPicker.currentVersion; @@ -248,13 +279,13 @@ export default class TypeScriptServiceClient extends Disposable implements IType currentVersion = this.versionPicker.currentVersion; } - this._apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion; + const apiVersion = this.versionPicker.currentVersion.version || API.defaultVersion; this.onDidChangeTypeScriptVersion(currentVersion); - this.lastError = null; let mytoken = ++this.token; const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager); + this.serverState = new ServerState.Running(handle, apiVersion, undefined); this.lastStart = Date.now(); handle.onError((err: Error) => { @@ -267,7 +298,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType vscode.window.showErrorMessage(localize('serverExitedWithError', 'TypeScript language server exited with error. Error message is: {0}', err.message || err.name)); } - this.lastError = err; + this.serverState = new ServerState.Errored(err); this.error('TSServer errored with error.', err); if (handle.tsServerLogFile) { this.error(`TSServer log file: ${handle.tsServerLogFile}`); @@ -282,7 +313,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType */ this.logTelemetry('tsserver.error'); this.serviceExited(false); - this.resetClientVersion(); }); handle.onExit((code: any) => { @@ -317,16 +347,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType handle.onEvent(event => this.dispatchEvent(event)); this._onReady!.resolve(); - this.forkedTsServer = handle; this._onTsServerStarted.fire(currentVersion.version); - if (this._apiVersion.gte(API.v300)) { + if (apiVersion.gte(API.v300)) { this.loadingIndicator.startedLoadingProject(undefined /* projectName */); } this.serviceStarted(resendModels); - return handle; + return this.serverState; } public onVersionStatusClicked(): Thenable { @@ -372,7 +401,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return false; } - if (!this.forkedTsServer || !this.forkedTsServer.tsServerLogFile) { + if (this.serverState.type !== ServerState.Type.Running || !this.serverState.server.tsServerLogFile) { vscode.window.showWarningMessage(localize( 'typescript.openTsServerLog.noLogFile', 'TS Server has not started logging.')); @@ -380,7 +409,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } try { - await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(this.forkedTsServer.tsServerLogFile)); + await vscode.commands.executeCommand('revealFileInOS', vscode.Uri.file(this.serverState.server.tsServerLogFile)); return true; } catch { vscode.window.showWarningMessage(localize( @@ -437,10 +466,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType id: MessageAction; } - this.forkedTsServer = null; - if (!restart) { - this.resetClientVersion(); - } else { + this.serverState = ServerState.None; + if (restart) { const diff = Date.now() - this.lastStart; this.numberRestarts++; let startService = true; @@ -464,7 +491,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType } */ this.logTelemetry('serviceExited'); - this.resetClientVersion(); } else if (diff < 60 * 1000 /* 1 Minutes */) { this.lastStart = Date.now(); prompt = vscode.window.showWarningMessage( @@ -490,7 +516,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } public normalizedPath(resource: vscode.Uri): string | undefined { - if (this._apiVersion.gte(API.v213)) { + if (this.apiVersion.gte(API.v213)) { if (resource.scheme === fileSchemes.walkThroughSnippet || resource.scheme === fileSchemes.untitled) { const dirName = path.dirname(resource.path); const fileName = this.inMemoryResourcePrefix + path.basename(resource.path); @@ -524,11 +550,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private get inMemoryResourcePrefix(): string { - return this._apiVersion.gte(API.v270) ? '^' : ''; + return this.apiVersion.gte(API.v270) ? '^' : ''; } public toResource(filepath: string): vscode.Uri { - if (this._apiVersion.gte(API.v213)) { + if (this.apiVersion.gte(API.v213)) { if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':')) ) { let resource = vscode.Uri.parse(filepath); @@ -694,7 +720,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; } if (telemetryData.telemetryEventName === 'projectInfo') { - this._tsserverVersion = properties['version']; + if (this.serverState.type === ServerState.Type.Running) { + this.serverState = new ServerState.Running(this.serverState.server, this.serverState.apiVersion, properties['version']); + } } /* __GDPR__ @@ -711,13 +739,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.logTelemetry(telemetryData.telemetryEventName, properties); } - private resetClientVersion() { - this._apiVersion = API.defaultVersion; - this._tsserverVersion = undefined; - } private configurePlugin(pluginName: string, configuration: {}): any { - if (this._apiVersion.gte(API.v314)) { + if (this.apiVersion.gte(API.v314)) { this.executeWithoutWaitingForResponse('configurePlugin', { pluginName, configuration }); } } From e730375b7a2b8c13edef579197baf95768ca1e52 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 14:09:49 -0800 Subject: [PATCH 0209/1066] Don't make semantic requests against the TS server when the languageService has been disabled --- .../src/tsServer/server.ts | 13 ++-- .../src/typescriptService.ts | 7 +- .../src/typescriptServiceClient.ts | 71 +++++++++++++------ .../contrib/codeAction/codeActionModel.ts | 2 +- 4 files changed, 63 insertions(+), 30 deletions(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index db7810ad96f..f6070274293 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 } from '../typescriptService'; +import { CancelledResponse, NoContentResponse, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from '../utils/configuration'; import { Disposable } from '../utils/dispose'; @@ -307,7 +307,9 @@ export class TypeScriptServer extends Disposable { } } - 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: 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 { const request = this._requestQueue.createRequest(command, args); const requestInfo: RequestItem = { request, @@ -315,9 +317,9 @@ export class TypeScriptServer extends Disposable { isAsync: executeInfo.isAsync, queueingType: getQueueingType(command, executeInfo.lowPriority) }; - let result: Promise; + 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) { @@ -346,9 +348,8 @@ export class TypeScriptServer extends Disposable { throw err; }); - } else { - result = Promise.resolve(null); } + this._requestQueue.enqueue(requestInfo); this.sendNextRequests(); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index db791db8fc1..eb48a67b99a 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -20,10 +20,11 @@ export class CancelledResponse { } export const NoContentResponse = new class { readonly type = 'noContent'; }; +export const LanguageServiceDisabledContentResponse = new class { readonly type = 'languageServiceDisabled'; }; -export type ServerResponse = T | CancelledResponse | typeof NoContentResponse; +export type ServerResponse = T | CancelledResponse | typeof NoContentResponse | typeof LanguageServiceDisabledContentResponse; -interface TypeScriptRequestTypes { +export interface TypeScriptRequestTypes { 'applyCodeActionCommand': [Proto.ApplyCodeActionCommandRequestArgs, Proto.ApplyCodeActionCommandResponse]; 'completionEntryDetails': [Proto.CompletionDetailsRequestArgs, Proto.CompletionDetailsResponse]; 'completionInfo': [Proto.CompletionsRequestArgs, Proto.CompletionInfoResponse]; @@ -110,7 +111,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 864d028f6fe..bc0a58ac97c 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 } from './typescriptService'; +import { ITypeScriptServiceClient, ServerResponse, LanguageServiceDisabledContentResponse } from './typescriptService'; import API from './utils/api'; import { TsServerLogLevel, TypeScriptServiceConfiguration } from './utils/configuration'; import { Disposable } from './utils/dispose'; @@ -57,6 +57,7 @@ namespace ServerState { * Version reported by currently-running tsserver. */ public tsserverVersion: string | undefined, + public langaugeServiceEnabled: boolean, ) { } } @@ -243,16 +244,16 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.telemetryReporter.logTelemetry(eventName, properties); } - private service(): TypeScriptServer { + private service(): ServerState.Running { if (this.serverState.type === ServerState.Type.Running) { - return this.serverState.server; + return this.serverState; } if (this.serverState.type === ServerState.Type.Errored) { throw this.serverState.error; } const newState = this.startService(); if (newState.type === ServerState.Type.Running) { - return newState.server; + return newState; } throw new Error('Could not create TS service'); } @@ -285,7 +286,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType let mytoken = ++this.token; const handle = this.typescriptServerSpawner.spawn(currentVersion, this.configuration, this.pluginManager); - this.serverState = new ServerState.Running(handle, apiVersion, undefined); + this.serverState = new ServerState.Running(handle, apiVersion, undefined, true); this.lastStart = Date.now(); handle.onError((err: Error) => { @@ -589,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, @@ -606,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, @@ -614,12 +615,30 @@ 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 { - const server = this.service(); - if (!server) { - return Promise.reject(new Error('Could not load TS Server')); + 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 { + 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(LanguageServiceDisabledContentResponse); + } } - return server.executeImpl(command, args, executeInfo); + + return runningServerState.server.executeImpl(command, args, executeInfo); } public interuptGetErr(f: () => R): R { @@ -649,14 +668,23 @@ export default class TypeScriptServiceClient extends Disposable implements IType break; case 'telemetry': - const telemetryData = (event as Proto.TelemetryEvent).body; - this.dispatchTelemetryEvent(telemetryData); - break; - + { + const body = (event as Proto.TelemetryEvent).body; + this.dispatchTelemetryEvent(body); + break; + } case 'projectLanguageServiceState': - this._onProjectLanguageServiceStateChanged.fire((event as Proto.ProjectLanguageServiceStateEvent).body); - break; - + { + const body = (event as Proto.ProjectLanguageServiceStateEvent).body!; + if (this.serverState.type === ServerState.Type.Running) { + this.serverState = { + ...this.serverState, + langaugeServiceEnabled: body.languageServiceEnabled, + }; + } + this._onProjectLanguageServiceStateChanged.fire(body); + break; + } case 'projectsUpdatedInBackground': const body = (event as Proto.ProjectsUpdatedInBackgroundEvent).body; const resources = body.openFiles.map(vscode.Uri.file); @@ -721,7 +749,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType } if (telemetryData.telemetryEventName === 'projectInfo') { if (this.serverState.type === ServerState.Type.Running) { - this.serverState = new ServerState.Running(this.serverState.server, this.serverState.apiVersion, properties['version']); + this.serverState = { + ...this.serverState, + tsserverVersion: properties['version'] + }; } } diff --git a/src/vs/editor/contrib/codeAction/codeActionModel.ts b/src/vs/editor/contrib/codeAction/codeActionModel.ts index aa08a4c20db..ff79983d255 100644 --- a/src/vs/editor/contrib/codeAction/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/codeActionModel.ts @@ -206,7 +206,7 @@ export class CodeActionModel { } if (this._state.type === CodeActionsState.Type.Triggered) { - this._state.actions.cancel(); + // this._state.actions.cancel(); } this.setState(CodeActionsState.Empty); From beef295f9ad1340d9ef0789ccd0ea780155eeb1e Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 14:23:01 -0800 Subject: [PATCH 0210/1066] Normalize the TS server path in logged TS server errors --- .../src/tsServer/server.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index f6070274293..b657449ee32 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -17,6 +17,7 @@ import LogDirectoryProvider from '../utils/logDirectoryProvider'; import Logger from '../utils/logger'; import { TypeScriptPluginPathsProvider } from '../utils/pluginPathsProvider'; import { PluginManager } from '../utils/plugins'; +import { escapeRegExp } from '../utils/regexp'; import TelemetryReporter from '../utils/telemetry'; import Tracer from '../utils/tracer'; import { TypeScriptVersion, TypeScriptVersionProvider } from '../utils/versionProvider'; @@ -30,7 +31,20 @@ class TypeScriptServerError extends Error { version: TypeScriptVersion, public readonly response: Proto.Response, ) { - super(`TypeScript Server Error (${version.versionString})\n${response.message}`); + super(`TypeScript Server Error (${version.versionString})\n${TypeScriptServerError.normalizeMessageStack(version, response.message)}`); + } + + /** + * Try to replace full TS Server paths with 'tsserver.js' so that we don't have to post process the data as much + */ + private static normalizeMessageStack( + version: TypeScriptVersion, + message: string | undefined, + ) { + if (!message) { + return message; + } + return message.replace(new RegExp(`${escapeRegExp(version.path)}[/\\\\]tsserver.js:`, 'gi'), 'tsserver.js:'); } } From b6715540e74bd7bf2b81924a4a0957d5512c2e50 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 14:38:41 -0800 Subject: [PATCH 0211/1066] Use for-of loops in place of some common index based loops --- src/vs/base/parts/tree/browser/treeView.ts | 3 +-- src/vs/editor/browser/controller/mouseTarget.ts | 6 ++---- .../viewParts/contentWidgets/contentWidgets.ts | 15 +++++---------- src/vs/editor/browser/viewParts/lines/viewLine.ts | 3 +-- .../viewParts/overviewRuler/overviewRuler.ts | 3 +-- src/vs/editor/browser/widget/codeEditorWidget.ts | 3 +-- src/vs/editor/common/model/mirrorTextModel.ts | 3 +-- .../common/viewModel/splitLinesCollection.ts | 9 +++------ src/vs/editor/common/viewModel/viewModelImpl.ts | 3 +-- src/vs/editor/contrib/find/findDecorations.ts | 3 +-- .../contrib/linesOperations/linesOperations.ts | 3 +-- .../editor/standalone/browser/simpleServices.ts | 3 +-- .../keybinding/common/keybindingResolver.ts | 3 +-- .../bulkEdit/electron-browser/bulkEditService.ts | 3 +-- .../extensionHostProcessManager.ts | 3 +-- .../electron-browser/extensionService.ts | 3 +-- .../electron-browser/keybindingService.ts | 6 ++---- .../textMate/electron-browser/TMSyntax.ts | 3 +-- 18 files changed, 26 insertions(+), 52 deletions(-) diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index c61ec47be8b..4c04f288b19 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -987,8 +987,7 @@ export class TreeView extends HeightMap { // 50 is an optimization number, at some point we're better off // just replacing everything if (!skipDiff && !doToInsertItemsAlreadyExist && diff.length < 50) { - for (let i = 0, len = diff.length; i < len; i++) { - const diffChange = diff[i]; + for (const diffChange of diff) { if (diffChange.originalLength > 0) { this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 3fa9c88ab27..54127e9beb6 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -539,8 +539,7 @@ export class MouseTargetFactory { // Check if we've hit a painted cursor const lastViewCursorsRenderData = ctx.lastViewCursorsRenderData; - for (let i = 0, len = lastViewCursorsRenderData.length; i < len; i++) { - const d = lastViewCursorsRenderData[i]; + for (const d of lastViewCursorsRenderData) { if (request.target === d.domNode) { return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position); @@ -558,8 +557,7 @@ export class MouseTargetFactory { const mouseContentHorizontalOffset = request.mouseContentHorizontalOffset; const mouseVerticalOffset = request.mouseVerticalOffset; - for (let i = 0, len = lastViewCursorsRenderData.length; i < len; i++) { - const d = lastViewCursorsRenderData[i]; + for (const d of lastViewCursorsRenderData) { if (mouseContentHorizontalOffset < d.contentLeft) { // mouse position is to the left of the cursor diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index e63b8d4a1f2..d31268fdf88 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -60,8 +60,7 @@ export class ViewContentWidgets extends ViewPart { public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { let keys = Object.keys(this._widgets); - for (let i = 0, len = keys.length; i < len; i++) { - const widgetId = keys[i]; + for (const widgetId of keys) { this._widgets[widgetId].onConfigurationChanged(e); } return true; @@ -75,8 +74,7 @@ export class ViewContentWidgets extends ViewPart { } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { let keys = Object.keys(this._widgets); - for (let i = 0, len = keys.length; i < len; i++) { - const widgetId = keys[i]; + for (const widgetId of keys) { this._widgets[widgetId].onLineMappingChanged(e); } return true; @@ -142,24 +140,21 @@ export class ViewContentWidgets extends ViewPart { public onBeforeRender(viewportData: ViewportData): void { let keys = Object.keys(this._widgets); - for (let i = 0, len = keys.length; i < len; i++) { - const widgetId = keys[i]; + for (const widgetId of keys) { this._widgets[widgetId].onBeforeRender(viewportData); } } public prepareRender(ctx: RenderingContext): void { let keys = Object.keys(this._widgets); - for (let i = 0, len = keys.length; i < len; i++) { - const widgetId = keys[i]; + for (const widgetId of keys) { this._widgets[widgetId].prepareRender(ctx); } } public render(ctx: RestrictedRenderingContext): void { let keys = Object.keys(this._widgets); - for (let i = 0, len = keys.length; i < len; i++) { - const widgetId = keys[i]; + for (const widgetId of keys) { this._widgets[widgetId].render(ctx); } } diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 1467d539a39..67a3cfc6392 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -173,8 +173,7 @@ export class ViewLine implements IVisibleLine { if (alwaysRenderInlineSelection || options.themeType === HIGH_CONTRAST) { const selections = viewportData.selections; - for (let i = 0, len = selections.length; i < len; i++) { - const selection = selections[i]; + for (const selection of selections) { if (selection.endLineNumber < lineNumber || selection.startLineNumber > lineNumber) { // Selection does not intersect line diff --git a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts index 6fe2cdf9a6c..748a3488d04 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/overviewRuler.ts @@ -132,8 +132,7 @@ export class OverviewRuler extends ViewEventHandler implements IOverviewRuler { let currentFrom = 0; let currentTo = 0; - for (let i = 0, len = colorZones.length; i < len; i++) { - const zone = colorZones[i]; + for (const zone of colorZones) { const zoneColorId = zone.colorId; const zoneFrom = zone.from; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index d7ba9754138..ae185d9d4e5 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -801,8 +801,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const contributionsState: { [key: string]: any } = {}; const keys = Object.keys(this._contributions); - for (let i = 0, len = keys.length; i < len; i++) { - const id = keys[i]; + for (const id of keys) { const contribution = this._contributions[id]; if (typeof contribution.saveViewState === 'function') { contributionsState[id] = contribution.saveViewState(); diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/model/mirrorTextModel.ts index c28d8febfc1..25b3d150ce0 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/model/mirrorTextModel.ts @@ -60,8 +60,7 @@ export class MirrorTextModel { // Update my lines const changes = e.changes; - for (let i = 0, len = changes.length; i < len; i++) { - const change = changes[i]; + for (const change of changes) { this._acceptDeleteRange(change.range); this._acceptInsertText(new Position(change.range.startLineNumber, change.range.startColumn), change.text); } diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index 70cf7650a48..13bf1b6f800 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -788,8 +788,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); const result = new OverviewRulerDecorations(); - for (let i = 0, len = decorations.length; i < len; i++) { - const decoration = decorations[i]; + for (const decoration of decorations) { const opts = decoration.options.overviewRuler; const lane = opts ? opts.position : 0; if (lane === 0) { @@ -858,8 +857,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection { // Eliminate duplicate decorations that might have intersected our visible ranges multiple times let finalResult: IModelDecoration[] = [], finalResultLen = 0; let prevDecId: string | null = null; - for (let i = 0, len = result.length; i < len; i++) { - const dec = result[i]; + for (const dec of result) { const decId = dec.id; if (prevDecId === decId) { // skip @@ -1380,8 +1378,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { public getAllOverviewRulerDecorations(ownerId: number, filterOutValidation: boolean, theme: ITheme): IOverviewRulerDecorations { const decorations = this.model.getOverviewRulerDecorations(ownerId, filterOutValidation); const result = new OverviewRulerDecorations(); - for (let i = 0, len = decorations.length; i < len; i++) { - const decoration = decorations[i]; + for (const decoration of decorations) { const opts = decoration.options.overviewRuler; const lane = opts ? opts.position : 0; if (lane === 0) { diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 3ce877d8212..23bfb971b84 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -537,8 +537,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel public invalidateOverviewRulerColorCache(): void { const decorations = this.model.getOverviewRulerDecorations(); - for (let i = 0, len = decorations.length; i < len; i++) { - const decoration = decorations[i]; + for (const decoration of decorations) { const opts = decoration.options.overviewRuler; if (opts) { opts.invalidateCachedColor(); diff --git a/src/vs/editor/contrib/find/findDecorations.ts b/src/vs/editor/contrib/find/findDecorations.ts index 10222c0ab3e..6c90044a369 100644 --- a/src/vs/editor/contrib/find/findDecorations.ts +++ b/src/vs/editor/contrib/find/findDecorations.ts @@ -80,8 +80,7 @@ export class FindDecorations implements IDisposable { public getCurrentMatchesPosition(desiredRange: Range): number { let candidates = this._editor.getModel().getDecorationsInRange(desiredRange); - for (let i = 0, len = candidates.length; i < len; i++) { - const candidate = candidates[i]; + for (const candidate of candidates) { const candidateOpts = candidate.options; if (candidateOpts === FindDecorations._FIND_MATCH_DECORATION || candidateOpts === FindDecorations._CURRENT_FIND_MATCH_DECORATION) { return this._getDecorationIndex(candidate.id); diff --git a/src/vs/editor/contrib/linesOperations/linesOperations.ts b/src/vs/editor/contrib/linesOperations/linesOperations.ts index 975bf3d23b1..3f254415f7b 100644 --- a/src/vs/editor/contrib/linesOperations/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/linesOperations.ts @@ -181,8 +181,7 @@ export abstract class AbstractSortLinesAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { const selections = editor.getSelections() || []; - for (let i = 0, len = selections.length; i < len; i++) { - const selection = selections[i]; + for (const selection of selections) { if (!SortLinesCommand.canRun(editor.getModel(), selection, this.descending)) { return; } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index f083e2ce44c..7babe1e4863 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -340,8 +340,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; - for (let i = 0, len = items.length; i < len; i++) { - const item = items[i]; + for (const item of items) { const when = (item.when ? item.when.normalize() : null); const keybinding = item.keybinding; diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 05cecccff7f..4e2ee339287 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -77,8 +77,7 @@ export class KeybindingResolver { public static combine(defaults: ResolvedKeybindingItem[], rawOverrides: ResolvedKeybindingItem[]): ResolvedKeybindingItem[] { defaults = defaults.slice(0); let overrides: ResolvedKeybindingItem[] = []; - for (let i = 0, len = rawOverrides.length; i < len; i++) { - const override = rawOverrides[i]; + for (const override of rawOverrides) { if (!override.command || override.command.length === 0 || override.command.charAt(0) !== '-') { overrides.push(override); continue; diff --git a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts index d570f8712de..1183cb36a24 100644 --- a/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts +++ b/src/vs/workbench/services/bulkEdit/electron-browser/bulkEditService.ts @@ -391,8 +391,7 @@ export class BulkEditService implements IBulkEditService { let codeEditor = options.editor; // First check if loaded models were not changed in the meantime - for (let i = 0, len = edits.length; i < len; i++) { - const edit = edits[i]; + for (const edit of edits) { if (!isResourceFileEdit(edit) && typeof edit.modelVersionId === 'number') { let model = this._modelService.getModel(edit.resource); if (model && model.getVersionId() !== edit.modelVersionId) { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index ba1ad93bebd..753b4782f1c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -187,8 +187,7 @@ export class ExtensionHostProcessManager extends Disposable { // Customers const customers = ExtHostCustomersRegistry.getCustomers(); - for (let i = 0, len = customers.length; i < len; i++) { - const ctor = customers[i]; + for (const ctor of customers) { const instance = this._instantiationService.createInstance(ctor, extHostContext); this._extensionHostProcessCustomers.push(instance); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index c30fbead131..498bc475c95 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -357,8 +357,7 @@ export class ExtensionService extends Disposable implements IExtensionService { let result: { [id: string]: IExtensionsStatus; } = Object.create(null); if (this._registry) { const extensions = this._registry.getAllExtensionDescriptions(); - for (let i = 0, len = extensions.length; i < len; i++) { - const extension = extensions[i]; + for (const extension of extensions) { const extensionKey = ExtensionIdentifier.toKey(extension.identifier); result[extension.identifier.value] = { messages: this._extensionsMessages.get(extensionKey), diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 57943ff811d..ea6946f0742 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -385,8 +385,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; - for (let i = 0, len = items.length; i < len; i++) { - const item = items[i]; + for (const item of items) { const when = (item.when ? item.when.normalize() : null); const keybinding = item.keybinding; if (!keybinding) { @@ -405,8 +404,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { private _resolveUserKeybindingItems(items: IUserKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; - for (let i = 0, len = items.length; i < len; i++) { - const item = items[i]; + for (const item of items) { const when = (item.when ? item.when.normalize() : null); const firstPart = item.firstPart; const chordPart = item.chordPart; diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 520ba3d13a4..3b946bdc74d 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -110,8 +110,7 @@ export class TMLanguageRegistration { if (tokenTypes) { // If tokenTypes is configured, fill in `this._tokenTypes` const scopes = Object.keys(tokenTypes); - for (let i = 0, len = scopes.length; i < len; i++) { - const scope = scopes[i]; + for (const scope of scopes) { const tokenType = tokenTypes[scope]; switch (tokenType) { case 'string': From adb4b04bfd42846abcbe88c93b4536428051cbed Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 14:40:17 -0800 Subject: [PATCH 0212/1066] Fix type error --- .../src/commands/goToProjectConfiguration.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts index 61729eafa8b..ee819648000 100644 --- a/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts +++ b/extensions/typescript-language-features/src/commands/goToProjectConfiguration.ts @@ -10,6 +10,7 @@ import { nulToken } from '../utils/cancellation'; import { Command } from '../utils/commandManager'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile, openOrCreateConfigFile } from '../utils/tsconfig'; +import { ServerResponse } from '../typescriptService'; const localize = nls.loadMessageBundle(); @@ -68,13 +69,13 @@ async function goToProjectConfig( return; } - let res: protocol.ProjectInfoResponse | undefined; + let res: ServerResponse | undefined; try { res = await client.execute('projectInfo', { file, needFileNameList: false }, nulToken); } catch { // noop } - if (!res || !res.body) { + if (!res || res.type !== 'response' || !res.body) { vscode.window.showWarningMessage(localize('typescript.projectConfigCouldNotGetInfo', 'Could not determine TypeScript or JavaScript project')); return; } From d3d16d2b349f126e6c42d8e19311d5fd727dfe00 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 15:32:15 -0800 Subject: [PATCH 0213/1066] Don't return symbolic columns for webview panels Fixes #56097 When a symbolic column is used while creating a webview panel, instead set the `viewColum` as `undefined` and then resolve it once we know the real (non-symbolic) column --- .../src/singlefolder-tests/webview.test.ts | 32 +++++++++++++++++++ src/vs/workbench/api/node/extHostWebview.ts | 7 +++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts index d211db9565b..1139fa54636 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/webview.test.ts @@ -307,6 +307,38 @@ suite('Webview tests', () => { assert.strictEqual((await response).value, false); } }); + + test('webviews should have real view column after they are created, #56097', async () => { + const webview = _register(vscode.window.createWebviewPanel(webviewId, 'title', { viewColumn: vscode.ViewColumn.Active }, { enableScripts: true })); + + // Since we used a symbolic column, we don't know what view column the webview will actually show in at first + assert.strictEqual(webview.viewColumn, undefined); + + let changed = false; + const viewStateChanged = new Promise((resolve) => { + webview.onDidChangeViewState(e => { + if (changed) { + throw new Error('Only expected a single view state change'); + } + changed = true; + resolve(e); + }, undefined, disposables); + }); + + assert.strictEqual((await viewStateChanged).webviewPanel.viewColumn, vscode.ViewColumn.One); + + const firstResponse = getMesssage(webview); + webview.webview.html = createHtmlDocumentWithBody(/*html*/` + `); + + webview.webview.postMessage({ value: 1 }); + await firstResponse; + assert.strictEqual(webview.viewColumn, vscode.ViewColumn.One); + + }); }); function createHtmlDocumentWithBody(body: string): string { diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index 808d6adffef..a39904bd0ff 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -171,8 +171,13 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { return this._options; } - get viewColumn(): vscode.ViewColumn { + get viewColumn(): vscode.ViewColumn | undefined { this.assertNotDisposed(); + if (this._viewColumn < 0) { + // We are using a symbolic view column + // Return undefined instead to indicate that the real view column is currently unknown but will be resolved. + return undefined; + } return this._viewColumn; } From 2ab82c12ed809fd1df921d016eed344a18a9ee8f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 11 Jan 2019 15:42:03 -0800 Subject: [PATCH 0214/1066] Make sure we opt in to new TS user preferences for enabling rename features Fixes #66176 --- .../src/features/fileConfigurationManager.ts | 6 ++++-- .../typescript-language-features/src/features/rename.ts | 9 +++++++-- .../typescript-language-features/src/languageProvider.ts | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts index d93bdce3367..794396595b2 100644 --- a/extensions/typescript-language-features/src/features/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/features/fileConfigurationManager.ts @@ -186,8 +186,10 @@ export default class FileConfigurationManager { return { quotePreference: getQuoteStylePreference(preferences), importModuleSpecifierPreference: getImportModuleSpecifierPreference(preferences), - allowTextChangesInNewFiles: document.uri.scheme === 'file' - }; + allowTextChangesInNewFiles: document.uri.scheme === 'file', + providePrefixAndSuffixTextForRename: true, + allowRenameOfImportPath: true, + } as Proto.UserPreferences; } } diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index 7112cb91867..ebcc30a8c68 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -10,12 +10,14 @@ import * as Proto from '../protocol'; import { ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; import API from '../utils/api'; import * as typeConverters from '../utils/typeConverters'; +import FileConfigurationManager from './fileConfigurationManager'; const localize = nls.loadMessageBundle(); class TypeScriptRenameProvider implements vscode.RenameProvider { public constructor( - private readonly client: ITypeScriptServiceClient + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager ) { } public async prepareRename( @@ -90,6 +92,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { }; return this.client.interuptGetErr(() => { + this.fileConfigurationManager.ensureConfigurationForDocument(document, token); return this.client.execute('rename', args, token); }); } @@ -143,6 +146,8 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { export function register( selector: vscode.DocumentSelector, client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, ) { - return vscode.languages.registerRenameProvider(selector, new TypeScriptRenameProvider(client)); + return vscode.languages.registerRenameProvider(selector, + new TypeScriptRenameProvider(client, fileConfigurationManager)); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 51f3b91beaf..dc25b87ee73 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -71,7 +71,7 @@ export default class LanguageProvider extends Disposable { this._register((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); this._register((await import('./features/references')).register(selector, this.client)); this._register((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); - this._register((await import('./features/rename')).register(selector, this.client)); + this._register((await import('./features/rename')).register(selector, this.client, this.fileConfigurationManager)); this._register((await import('./features/signatureHelp')).register(selector, this.client)); this._register((await import('./features/tagClosing')).register(selector, this.description.id, this.client)); this._register((await import('./features/typeDefinitions')).register(selector, this.client)); From 9b65b4946cf410c0993504dcb49b654083f22311 Mon Sep 17 00:00:00 2001 From: Sana Ajani Date: Fri, 11 Jan 2019 18:21:57 -0800 Subject: [PATCH 0215/1066] python azure tagging fixes --- .../parts/stats/node/workspaceStats.ts | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/stats/node/workspaceStats.ts b/src/vs/workbench/parts/stats/node/workspaceStats.ts index d98b9c06057..d42cc242bf4 100644 --- a/src/vs/workbench/parts/stats/node/workspaceStats.ts +++ b/src/vs/workbench/parts/stats/node/workspaceStats.ts @@ -71,7 +71,6 @@ const PyModulesToLookFor = [ 'azure-storage-blob', 'azure-storage-file', 'azure-storage-queue', - 'azure-mgmt', 'azure-shell', 'azure-cosmos', 'azure-devtools', @@ -79,14 +78,19 @@ const PyModulesToLookFor = [ 'azure-eventgrid', 'azure-functions', 'azure-graphrbac', - 'azure-keybault', + 'azure-keyvault', 'azure-loganalytics', 'azure-monitor', 'azure-servicebus', 'azure-servicefabric', 'azure-storage', 'azure-translator', - 'azure-iothub-device-client' + 'azure-iothub-device-client', + 'adal', + 'pydocumentdb', + 'botbuilder-core', + 'botbuilder-schema', + 'botframework-connector' ]; type Tags = { [index: string]: boolean | number | string | undefined }; @@ -299,7 +303,7 @@ export class WorkspaceStats implements IWorkbenchContribution { "workspace.py.azure-eventgrid" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-functions" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-graphrbac" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspace.py.azure-keybault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.azure-keyvault" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-loganalytics" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-monitor" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.azure-servicebus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -307,7 +311,14 @@ 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.botframework-connector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } */ private resolveWorkspaceTags(configuration: IWindowConfiguration, participant?: (rootFiles: string[]) => void): Promise { @@ -433,7 +444,13 @@ export class WorkspaceStats implements IWorkbenchContribution { } // cognitive services has a lot of tiny packages. eg. 'azure-cognitiveservices-search-autosuggest' if (packageName.indexOf('azure-cognitiveservices') > -1) { - tags['workspace.py.cognitiveservices'] = true; + tags['workspace.py.azure-cognitiveservices'] = true; + } + if (packageName.indexOf('azure-mgmt') > -1) { + tags['workspace.py.azure-mgmt'] = true; + } + if (packageName.indexOf('azure-ml') > -1) { + tags['workspace.py.azure-ml'] = true; } if (!tags['workspace.py.any-azure']) { tags['workspace.py.any-azure'] = /azure/i.test(packageName); @@ -441,7 +458,7 @@ export class WorkspaceStats implements IWorkbenchContribution { } const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, content => { - const dependencies: string[] = content.value.split('\r\n|\n'); + const dependencies: string[] = content.value.split(/\r\n|\r|\n/); for (let dependency of dependencies) { // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` const format1 = dependency.split('=='); @@ -452,7 +469,7 @@ export class WorkspaceStats implements IWorkbenchContribution { }); const pipfilePromises = getFilePromises('pipfile', this.fileService, content => { - let dependencies: string[] = content.value.split(/\r\n|\n/); + let dependencies: string[] = content.value.split(/\r\n|\r|\n/); // We're only interested in the '[packages]' section of the Pipfile dependencies = dependencies.slice(dependencies.indexOf('[packages]') + 1); From 47014f778fb57abf0c52449803c590b8c9cf0dec Mon Sep 17 00:00:00 2001 From: Yisrael Date: Sat, 12 Jan 2019 20:11:11 +0200 Subject: [PATCH 0216/1066] Make smart scroll and lock action work together. Clicking inside the output panel should set the lock on just as if the lock icon was clicked. Moving to the last line (with ctrl+end or click) should unset the lock. --- .../parts/editor/textResourceEditor.ts | 9 ++------ .../parts/output/browser/outputActions.ts | 15 ++++++++++--- .../parts/output/browser/outputPanel.ts | 21 ++++++++++++++++--- .../output/electron-browser/outputServices.ts | 17 ++------------- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index 32c2ff53afa..072e5a6f500 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -136,19 +136,14 @@ export class AbstractTextResourceEditor extends BaseTextEditor { /** * Reveals the last line of this editor if it has a model set. - * When smart is true only scroll if the cursor is currently on the last line of the output panel. - * This allows users to click on the output panel to stop scrolling when they see something of interest. - * To resume, they should scroll to the end of the output panel again. */ - revealLastLine(smart: boolean): void { + revealLastLine(): void { const codeEditor = this.getControl(); const model = codeEditor.getModel(); if (model) { const lastLine = model.getLineCount(); - if (!smart || codeEditor.getPosition().lineNumber === lastLine) { - codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth); - } + codeEditor.revealPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }, ScrollType.Smooth); } } diff --git a/src/vs/workbench/parts/output/browser/outputActions.ts b/src/vs/workbench/parts/output/browser/outputActions.ts index b36e961422e..f895e0bb3b0 100644 --- a/src/vs/workbench/parts/output/browser/outputActions.ts +++ b/src/vs/workbench/parts/output/browser/outputActions.ts @@ -57,7 +57,10 @@ export class ClearOutputAction extends Action { } } -export class ToggleOutputScrollLockAction extends Action { +// this action can be triggered in two ways: +// 1. user clicks the action icon, In which case the action toggles the lock state +// 2. user clicks inside the output panel, which sets the lock, Or unsets it if they click the last line. +export class ToggleOrSetOutputScrollLockAction extends Action { public static readonly ID = 'workbench.output.action.toggleOutputScrollLock'; public static readonly LABEL = nls.localize({ key: 'toggleOutputScrollLock', comment: ['Turn on / off automatic output scrolling'] }, "Toggle Output Scroll Lock"); @@ -70,10 +73,16 @@ export class ToggleOutputScrollLockAction extends Action { this.toDispose.push(this.outputService.onActiveOutputChannel(channel => this.setClass(this.outputService.getActiveChannel().scrollLock))); } - public run(): Promise { + public run(newLockState?: boolean): Promise { + const activeChannel = this.outputService.getActiveChannel(); if (activeChannel) { - activeChannel.scrollLock = !activeChannel.scrollLock; + if (typeof (newLockState) === 'boolean') { + activeChannel.scrollLock = newLockState; + } + else { + activeChannel.scrollLock = !activeChannel.scrollLock; + } this.setClass(activeChannel.scrollLock); } diff --git a/src/vs/workbench/parts/output/browser/outputPanel.ts b/src/vs/workbench/parts/output/browser/outputPanel.ts index 226d63fcf40..6c469924c54 100644 --- a/src/vs/workbench/parts/output/browser/outputPanel.ts +++ b/src/vs/workbench/parts/output/browser/outputPanel.ts @@ -7,6 +7,7 @@ import 'vs/css!./media/output'; import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -17,7 +18,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { AbstractTextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor'; import { OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT } from 'vs/workbench/parts/output/common/output'; -import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction, ToggleOutputScrollLockAction, OpenLogOutputFile } from 'vs/workbench/parts/output/browser/outputActions'; +import { SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction, ToggleOrSetOutputScrollLockAction, OpenLogOutputFile } from 'vs/workbench/parts/output/browser/outputActions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -25,6 +26,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorG import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; export class OutputPanel extends AbstractTextResourceEditor { private actions: IAction[]; @@ -63,7 +65,7 @@ export class OutputPanel extends AbstractTextResourceEditor { this.actions = [ this.instantiationService.createInstance(SwitchOutputAction), this.instantiationService.createInstance(ClearOutputAction, ClearOutputAction.ID, ClearOutputAction.LABEL), - this.instantiationService.createInstance(ToggleOutputScrollLockAction, ToggleOutputScrollLockAction.ID, ToggleOutputScrollLockAction.LABEL), + this.instantiationService.createInstance(ToggleOrSetOutputScrollLockAction, ToggleOrSetOutputScrollLockAction.ID, ToggleOrSetOutputScrollLockAction.LABEL), this.instantiationService.createInstance(OpenLogOutputFile) ]; @@ -126,7 +128,7 @@ export class OutputPanel extends AbstractTextResourceEditor { if (this._focus) { this.focus(); } - this.revealLastLine(false); + this.revealLastLine(); }); } @@ -145,6 +147,19 @@ export class OutputPanel extends AbstractTextResourceEditor { super.createEditor(parent); CONTEXT_IN_OUTPUT.bindTo(scopedContextKeyService).set(true); + + const codeEditor = this.getControl(); + codeEditor.onDidChangeCursorPosition((e) => { + if (e.reason !== CursorChangeReason.Explicit) { + return; + } + + const newPositionLine = e.position.lineNumber; + const lastLine = codeEditor.getModel().getLineCount(); + const newLockState = lastLine !== newPositionLine; + const lockAction = this.actions.filter((action) => action.id === ToggleOrSetOutputScrollLockAction.ID)[0]; + lockAction.run(newLockState); + }); } public get instantiationService(): IInstantiationService { diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index a7863fe0201..e83e6fe759d 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -10,7 +10,6 @@ import * as extfs from 'vs/base/node/extfs'; import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -540,16 +539,6 @@ export class OutputService extends Disposable implements IOutputService, ITextMo } } - private setPrimaryCursorToLastLine(): void { - const codeEditor = this._outputPanel.getControl(); - const model = codeEditor.getModel(); - - if (model) { - const lastLine = model.getLineCount(); - codeEditor.setPosition({ lineNumber: lastLine, column: model.getLineMaxColumn(lastLine) }); - } - } - private createChannel(id: string): OutputChannel { const channelDisposables: IDisposable[] = []; const channel = this.instantiateChannel(id); @@ -558,7 +547,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo const panel = this.panelService.getActivePanel(); if (panel && panel.getId() === OUTPUT_PANEL_ID && this.isChannelShown(channel)) { let outputPanel = panel; - outputPanel.revealLastLine(true); + outputPanel.revealLastLine(); } } }, channelDisposables); @@ -612,9 +601,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo if (!preserveFocus) { this._outputPanel.focus(); } - }) - // Activate smart scroll when switching back to the output panel - .then(() => this.setPrimaryCursorToLastLine()); + }); } return Promise.resolve(undefined); } From 09060e01ef3cf6cb3342e188e4c19c3fc38c2226 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 12 Jan 2019 16:48:52 -0800 Subject: [PATCH 0217/1066] Changing var to const --- .../workbench/parts/debug/test/browser/linkDetector.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts index 782956c5020..d97b1b83c0e 100644 --- a/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts +++ b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts @@ -75,9 +75,9 @@ suite('Debug - Link Detector', () => { }); test('singleLineLinkAndText', function () { - var input = isWindows ? 'The link: C:/foo/bar.js:12:34' : 'The link: /Users/foo/bar.js:12:34'; - var expectedOutput = /^The link: <\/span>.*\/foo\/bar.js:12:34<\/a><\/span>$/; - var output = linkDetector.handleLinks(input); + const input = isWindows ? 'The link: C:/foo/bar.js:12:34' : 'The link: /Users/foo/bar.js:12:34'; + const expectedOutput = /^The link: <\/span>.*\/foo\/bar.js:12:34<\/a><\/span>$/; + const output = linkDetector.handleLinks(input); assert.equal(2, output.children.length); assert.equal('SPAN', output.tagName); From bf06d8c1ac3a61cf354a94015af172311b06c740 Mon Sep 17 00:00:00 2001 From: xiaofa Date: Sun, 13 Jan 2019 09:34:33 +0800 Subject: [PATCH 0218/1066] add strict null checks to findModel.test.ts --- src/tsconfig.strictNullChecks.json | 1 + src/vs/editor/contrib/find/test/findModel.test.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 09d49339198..5e8aa52b6a9 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -186,6 +186,7 @@ "./vs/editor/contrib/find/simpleFindWidget.ts", "./vs/editor/contrib/find/test/find.test.ts", "./vs/editor/contrib/find/test/findController.test.ts", + "./vs/editor/contrib/find/test/findModel.test.ts", "./vs/editor/contrib/find/test/replacePattern.test.ts", "./vs/editor/contrib/folding/folding.ts", "./vs/editor/contrib/folding/foldingDecorations.ts", diff --git a/src/vs/editor/contrib/find/test/findModel.test.ts b/src/vs/editor/contrib/find/test/findModel.test.ts index bd93b102c43..fb34b82efa0 100644 --- a/src/vs/editor/contrib/find/test/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/findModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { CoreNavigationCommands } from 'vs/editor/browser/controller/coreCommands'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -18,7 +18,7 @@ import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; suite('FindModel', () => { - function findTest(testName: string, callback: (editor: ICodeEditor, cursor: Cursor) => void): void { + function findTest(testName: string, callback: (editor: IActiveCodeEditor, cursor: Cursor) => void): void { test(testName, () => { const textArr = [ '// my cool header', @@ -34,7 +34,7 @@ suite('FindModel', () => { '// blablablaciao', '' ]; - withTestCodeEditor(textArr, {}, callback); + withTestCodeEditor(textArr, {}, (editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor)); const text = textArr.join('\n'); const ptBuilder = new PieceTreeTextBufferBuilder(); @@ -45,7 +45,9 @@ suite('FindModel', () => { withTestCodeEditor([], { model: new TextModel(factory, TextModel.DEFAULT_CREATION_OPTIONS, null, null) - }, callback); + }, + (editor, cursor) => callback(editor as unknown as IActiveCodeEditor, cursor) + ); }); } From 8a490b8bda130c4b561f30a656e4d708d262480e Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 12 Jan 2019 21:13:33 -0800 Subject: [PATCH 0219/1066] Fix test for Windows --- src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts index d97b1b83c0e..efae57e5ca0 100644 --- a/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts +++ b/src/vs/workbench/parts/debug/test/browser/linkDetector.test.ts @@ -104,7 +104,7 @@ suite('Debug - Link Detector', () => { assertElementIsLink(output.children[1]); assertElementIsLink(output.children[3]); assert.equal(isWindows ? 'C:/foo/bar.js:12:34' : '/Users/foo/bar.js:12:34', output.children[1].textContent); - assert.equal(isWindows ? 'C:/boo/far.js:56:78' : '/Users/boo/far.js:56:78', output.children[3].textContent); + assert.equal(isWindows ? 'D:/boo/far.js:56:78' : '/Users/boo/far.js:56:78', output.children[3].textContent); }); test('multilineNoLinks', () => { From 3220a50550e3ccd54411b8a4862ccd2b967e9e97 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 14 Jan 2019 09:25:31 +0100 Subject: [PATCH 0220/1066] error when normalizing empty path --- src/vs/base/common/resources.ts | 3 +++ src/vs/base/test/common/resources.test.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index a10a3b01e74..af132e4179b 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -114,6 +114,9 @@ export function joinPath(resource: URI, pathFragment: string): URI { * @returns The URI with the normalized path. */ export function normalizePath(resource: URI): URI { + if (!resource.path.length) { + return resource; + } let normalizedPath: string; if (resource.scheme === Schemas.file) { normalizedPath = URI.file(paths.normalize(fsPath(resource))).path; diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index 9816adbbbe9..7bb9721d124 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -138,6 +138,7 @@ suite('Resources', () => { assert.equal(normalizePath(URI.file('/foo/foo/../../bar')).toString(), 'file:///bar'); assert.equal(normalizePath(URI.file('/foo/foo/./../../bar')).toString(), 'file:///bar'); assert.equal(normalizePath(URI.file('/foo/foo/./../some/../bar')).toString(), 'file:///foo/bar'); + assert.equal(normalizePath(URI.file('/f')).toString(), 'file:///f'); } assert.equal(normalizePath(URI.parse('foo://a/foo/./bar')).toString(), 'foo://a/foo/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/.')).toString(), 'foo://a/foo'); @@ -148,6 +149,8 @@ suite('Resources', () => { assert.equal(normalizePath(URI.parse('foo://a/foo/foo/../../bar')).toString(), 'foo://a/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../../bar')).toString(), 'foo://a/bar'); assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../some/../bar')).toString(), 'foo://a/foo/bar'); + assert.equal(normalizePath(URI.parse('foo://a')).toString(), 'foo://a'); + assert.equal(normalizePath(URI.parse('foo://a/')).toString(), 'foo://a/'); }); test('isAbsolute', () => { From b58541813573efdeb37af6984fa1db2491fbc841 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 14 Jan 2019 09:28:44 +0100 Subject: [PATCH 0221/1066] polish paths & resources --- src/vs/base/common/paths.ts | 49 ++++++++++++++++----------------- src/vs/base/common/resources.ts | 21 +++++++------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/vs/base/common/paths.ts b/src/vs/base/common/paths.ts index d18519a7731..26f885e6b7b 100644 --- a/src/vs/base/common/paths.ts +++ b/src/vs/base/common/paths.ts @@ -26,7 +26,8 @@ function isPathSeparator(code: number) { * @param path the path to get the dirname from * @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 */ export function dirname(path: string, separator = nativeSep): string { const len = path.length; @@ -38,7 +39,7 @@ export function dirname(path: string, separator = nativeSep): string { const root = getRoot(path, separator); let rootLength = root.length; if (rootLength >= len) { - return root; // matched the root + return path; // matched the root } if (rootLength === 0 && isPathSeparator(path.charCodeAt(0))) { rootLength = 1; // absolute paths stay absolute paths. @@ -119,7 +120,7 @@ export function normalize(path: string | null | undefined, toOSPath?: boolean): for (let end = root.length; end <= len; end++) { // either at the end or at a path-separator character - if (end === len || path.charCodeAt(end) === CharCode.Slash || path.charCodeAt(end) === CharCode.Backslash) { + if (end === len || isPathSeparator(path.charCodeAt(end))) { if (streql(path, start, end, '..')) { // skip current and remove parent (if there is already something) @@ -165,29 +166,23 @@ export function getRoot(path: string, sep: string = '/'): string { } let len = path.length; - let code = path.charCodeAt(0); - if (code === CharCode.Slash || code === CharCode.Backslash) { - - code = path.charCodeAt(1); - if (code === CharCode.Slash || code === CharCode.Backslash) { + const firstLetter = path.charCodeAt(0); + if (isPathSeparator(firstLetter)) { + if (isPathSeparator(path.charCodeAt(1))) { // UNC candidate \\localhost\shares\ddd // ^^^^^^^^^^^^^^^^^^^ - code = path.charCodeAt(2); - if (code !== CharCode.Slash && code !== CharCode.Backslash) { + if (!isPathSeparator(path.charCodeAt(2))) { let pos = 3; let start = pos; for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { break; } } - code = path.charCodeAt(pos + 1); - if (start !== pos && code !== CharCode.Slash && code !== CharCode.Backslash) { + if (start !== pos && !isPathSeparator(path.charCodeAt(pos + 1))) { pos += 1; for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { return path.slice(0, pos + 1) // consume this separator .replace(/[\\/]/g, sep); } @@ -200,12 +195,11 @@ export function getRoot(path: string, sep: string = '/'): string { // ^ return sep; - } else if ((code >= CharCode.A && code <= CharCode.Z) || (code >= CharCode.a && code <= CharCode.z)) { + } else if (isWindowsDriveLetter(firstLetter)) { // check for windows drive letter c:\ or c: if (path.charCodeAt(1) === CharCode.Colon) { - code = path.charCodeAt(2); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(2))) { // C:\fff // ^^^ return path.slice(0, 2) + sep; @@ -224,8 +218,7 @@ export function getRoot(path: string, sep: string = '/'): string { if (pos !== -1) { pos += 3; // 3 -> "://".length for (; pos < len; pos++) { - code = path.charCodeAt(pos); - if (code === CharCode.Slash || code === CharCode.Backslash) { + if (isPathSeparator(path.charCodeAt(pos))) { return path.slice(0, pos + 1); // consume this separator } } @@ -246,9 +239,9 @@ export const join: (...parts: string[]) => string = function () { // add the separater between two parts unless // there already is one let last = value.charCodeAt(value.length - 1); - if (last !== CharCode.Slash && last !== CharCode.Backslash) { + if (!isPathSeparator(last)) { let next = part.charCodeAt(0); - if (next !== CharCode.Slash && next !== CharCode.Backslash) { + if (!isPathSeparator(next)) { value += sep; } @@ -403,12 +396,12 @@ export function isAbsolute_win32(path: string): boolean { } const char0 = path.charCodeAt(0); - if (char0 === CharCode.Slash || char0 === CharCode.Backslash) { + if (isPathSeparator(char0)) { return true; - } else if ((char0 >= CharCode.A && char0 <= CharCode.Z) || (char0 >= CharCode.a && char0 <= CharCode.z)) { + } else if (isWindowsDriveLetter(char0)) { if (path.length > 2 && path.charCodeAt(1) === CharCode.Colon) { const char2 = path.charCodeAt(2); - if (char2 === CharCode.Slash || char2 === CharCode.Backslash) { + if (isPathSeparator(char2)) { return true; } } @@ -420,3 +413,7 @@ export function isAbsolute_win32(path: string): boolean { export function isAbsolute_posix(path: string): boolean { return !!(path && path.charCodeAt(0) === CharCode.Slash); } + +export function isWindowsDriveLetter(char0: number): boolean { + return char0 >= CharCode.A && char0 <= CharCode.Z || char0 >= CharCode.a && char0 <= CharCode.z; +} diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index af132e4179b..33d813365b4 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -73,12 +73,12 @@ export function basename(resource: URI): string { * @returns The URI representing the directory of the input URI. */ export function dirname(resource: URI): URI | null { - if (resource.scheme === Schemas.file) { - return URI.file(paths.dirname(fsPath(resource))); - } if (resource.path.length === 0) { return resource; } + if (resource.scheme === Schemas.file) { + return URI.file(paths.dirname(fsPath(resource))); + } let dirname = paths.dirname(resource.path, '/'); if (resource.authority && dirname.length && dirname.charCodeAt(0) !== CharCode.Slash) { return null; // If a URI contains an authority component, then the path component must either be empty or begin with a CharCode.Slash ("/") character @@ -134,19 +134,20 @@ export function normalizePath(resource: URI): URI { */ export function fsPath(uri: URI): string { let value: string; - if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { + const uriPath = uri.path; + if (uri.authority && uriPath.length > 1 && uri.scheme === 'file') { // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uri.path}`; + value = `//${uri.authority}${uriPath}`; } else if ( isWindows - && uri.path.charCodeAt(0) === CharCode.Slash - && (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z) - && uri.path.charCodeAt(2) === CharCode.Colon + && uriPath.charCodeAt(0) === CharCode.Slash + && paths.isWindowsDriveLetter(uriPath.charCodeAt(0)) + && uriPath.charCodeAt(2) === CharCode.Colon ) { - value = uri.path.substr(1); + value = uriPath.substr(1); } else { // other path - value = uri.path; + value = uriPath; } if (isWindows) { value = value.replace(/\//g, '\\'); From a3954fc17a979cd185e1c3458920ac409e707fd0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 14 Jan 2019 09:58:00 +0100 Subject: [PATCH 0222/1066] parseUri: bug with normalizing URI --- src/vs/code/electron-main/windows.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 0476bb34e4c..5b5bb7be830 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1016,8 +1016,9 @@ export class WindowsManager implements IWindowsMainService { // normalize URI uri = normalizePath(uri); - if (endsWith(uri.path, '/')) { - uri = uri.with({ path: uri.path.substr(0, uri.path.length - 1) }); + const uriPath = uri.path; + if (uriPath.length > 2 && endsWith(uriPath, '/')) { + uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) }); } if (isFile) { if (options && options.gotoLineMode) { From 91e42bf266a61113920178673fb415fb051ed17a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 14 Jan 2019 10:06:31 +0100 Subject: [PATCH 0223/1066] remove children resolution event --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 28 +++++--------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 4bbcc410b6d..823a1e055cd 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -115,16 +115,6 @@ function asTreeContextMenuEvent(e: ITreeContextMenuEvent { - readonly element: T | null; - readonly reason: ChildrenResolutionReason; -} - function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { const nodes = (data as ElementsDragAndDropData>).elements; @@ -233,9 +223,6 @@ export class AsyncDataTree implements IDisposable get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } get onDidOpen(): Event> { return Event.map(this.tree.onDidOpen, asTreeEvent); } - private readonly _onDidResolveChildren = new Emitter>(); - readonly onDidResolveChildren: Event> = this._onDidResolveChildren.event; - get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } @@ -340,7 +327,7 @@ export class AsyncDataTree implements IDisposable throw new Error('Tree input not set'); } - return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh); + return this.refreshNode(this.getDataNode(element), recursive); } // Tree @@ -365,7 +352,7 @@ export class AsyncDataTree implements IDisposable this.tree.expand(node, recursive); if (node.state !== AsyncDataTreeNodeState.Loaded) { - await this.refreshNode(node, false, ChildrenResolutionReason.Expand); + await this.refreshNode(node, false); } return true; @@ -489,15 +476,15 @@ export class AsyncDataTree implements IDisposable return node; } - private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { - await this.doRefresh(node, recursive, reason); + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean): Promise { + await this.doRefresh(node, recursive); if (recursive && node.children) { - await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason))); + await Promise.all(node.children.map(child => this.refreshNode(child, recursive))); } } - private doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { + private doRefresh(node: IAsyncDataTreeNode, recursive: boolean): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); if (!hasChildren) { @@ -523,7 +510,6 @@ export class AsyncDataTree implements IDisposable this._onDidChangeNodeState.fire(node); this.setChildren(node, children, recursive); - this._onDidResolveChildren.fire({ element: node.element as T, reason }); }, err => { if (isPromiseCanceledError(err)) { return Promise.resolve(null); @@ -559,7 +545,7 @@ export class AsyncDataTree implements IDisposable if (deep) { this.collapse(node.element.element as T); } else { - this.refreshNode(node.element, false, ChildrenResolutionReason.Expand); + this.refreshNode(node.element, false); } } } From 2f18659fdc1f9d3503fcdafd4a1ee98ca7bd7062 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 14 Jan 2019 10:41:43 +0100 Subject: [PATCH 0224/1066] async data tree: prevent conflicting operations fixes #66407 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 43 +++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 823a1e055cd..5cf1330feab 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -21,7 +21,8 @@ enum AsyncDataTreeNodeState { Uninitialized, Loaded, Loading, - Slow + Slow, + Disposed } interface IAsyncDataTreeNode { @@ -32,6 +33,16 @@ interface IAsyncDataTreeNode { state: AsyncDataTreeNodeState; } +function isAncestor(ancestor: IAsyncDataTreeNode, descendant: IAsyncDataTreeNode): boolean { + if (!descendant.parent) { + return false; + } else if (descendant.parent === ancestor) { + return true; + } else { + return isAncestor(ancestor, descendant.parent); + } +} + interface IDataTreeListTemplateData { templateData: T; } @@ -477,13 +488,39 @@ export class AsyncDataTree implements IDisposable } private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean): Promise { - await this.doRefresh(node, recursive); + await this.queueRefresh(node, recursive); if (recursive && node.children) { await Promise.all(node.children.map(child => this.refreshNode(child, recursive))); } } + private currentRefreshCalls = new Map, Promise>(); + + private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean): Promise { + if (node.state === AsyncDataTreeNodeState.Disposed) { + console.error('Async data tree node is disposed'); + return; + } + + let result: Promise | undefined; + + this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { + if (isAncestor(refreshNode, node) || isAncestor(node, refreshNode)) { + result = refreshPromise.then(() => this.queueRefresh(node, recursive)); + } + }); + + if (result) { + return result; + } + + result = this.doRefresh(node, recursive); + + this.currentRefreshCalls.set(node, result); + return always(result, () => this.currentRefreshCalls.delete(node)); + } + private doRefresh(node: IAsyncDataTreeNode, recursive: boolean): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); @@ -491,6 +528,7 @@ export class AsyncDataTree implements IDisposable this.setChildren(node, [], recursive); return Promise.resolve(); } else if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) { + node.state = AsyncDataTreeNodeState.Uninitialized; return Promise.resolve(); } else { node.state = AsyncDataTreeNodeState.Loading; @@ -636,6 +674,7 @@ export class AsyncDataTree implements IDisposable const onDidDeleteNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { if (!insertedElements.has(treeNode.element.element as T)) { + treeNode.element.state = AsyncDataTreeNodeState.Disposed; this.nodes.delete(treeNode.element.element as T); } } From fe9539a05ffb89bea781e7a4e7f7029980b30686 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 14 Jan 2019 10:43:03 +0100 Subject: [PATCH 0225/1066] resources.fsPath broken --- src/vs/base/common/resources.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 33d813365b4..9d236790f5e 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -141,7 +141,7 @@ export function fsPath(uri: URI): string { } else if ( isWindows && uriPath.charCodeAt(0) === CharCode.Slash - && paths.isWindowsDriveLetter(uriPath.charCodeAt(0)) + && paths.isWindowsDriveLetter(uriPath.charCodeAt(1)) && uriPath.charCodeAt(2) === CharCode.Colon ) { value = uriPath.substr(1); From 7090af71a002f7566abf73debd4295e35b01051f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 14 Jan 2019 11:12:44 +0100 Subject: [PATCH 0226/1066] async data tree: always check for root fixes #66408 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 13 +++++++------ .../files/electron-browser/views/explorerView.ts | 12 ++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 5cf1330feab..cf3972fc432 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -350,17 +350,18 @@ export class AsyncDataTree implements IDisposable } collapse(element: T, recursive: boolean = false): boolean { - return this.tree.collapse(this.getDataNode(element), recursive); + const node = this.getDataNode(element); + return this.tree.collapse(node === this.root ? null : node, recursive); } async expand(element: T, recursive: boolean = false): Promise { const node = this.getDataNode(element); - if (!this.tree.isCollapsed(node)) { + if (!this.tree.isCollapsed(node === this.root ? null : node)) { return false; } - this.tree.expand(node, recursive); + this.tree.expand(node === this.root ? null : node, recursive); if (node.state !== AsyncDataTreeNodeState.Loaded) { await this.refreshNode(node, false); @@ -558,7 +559,7 @@ export class AsyncDataTree implements IDisposable this._onDidChangeNodeState.fire(node); if (node !== this.root) { - this.tree.collapse(node); + this.tree.collapse(node === this.root ? null : node); } return Promise.reject(err); @@ -632,12 +633,12 @@ export class AsyncDataTree implements IDisposable asyncDataTreeNode.element = element; const collapsible = !!this.dataSource.hasChildren(element); - const collapsed = !collapsible || this.tree.isCollapsed(asyncDataTreeNode); + const collapsed = !collapsible || this.tree.isCollapsed(asyncDataTreeNode === this.root ? null : asyncDataTreeNode); if (recursive) { asyncDataTreeNode.state = AsyncDataTreeNodeState.Uninitialized; - if (this.tree.isCollapsed(asyncDataTreeNode)) { + if (this.tree.isCollapsed(asyncDataTreeNode === this.root ? null : asyncDataTreeNode)) { asyncDataTreeNode.children!.length = 0; return { 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 d56dc3b72db..492c178177d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import * as perf from 'vs/base/common/performance'; -import { sequence, ignoreErrors } from 'vs/base/common/async'; +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'; @@ -165,10 +165,14 @@ export class ExplorerView extends ViewletPanel { const isEditing = !!this.explorerService.getEditableData(e); if (isEditing) { this.tree.setFocus([]); - expandPromise = ignoreErrors(this.tree.expand(e.parent)); + expandPromise = this.tree.expand(e.parent); } DOM.toggleClass(this.tree.getHTMLElement(), 'highlight', isEditing); - expandPromise.then(() => this.refresh(e.parent)); + expandPromise.then(() => this.refresh(e.parent)).then(() => { + if (isEditing) { + this.tree.reveal(e); + } + }); })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); @@ -435,7 +439,7 @@ export class ExplorerView extends ViewletPanel { parent = parent.parent; } - return sequence(toExpand.reverse().map(s => () => ignoreErrors(this.tree.expand(s)))).then(() => { + return sequence(toExpand.reverse().map(s => () => this.tree.expand(s))).then(() => { if (reveal) { this.tree.reveal(fileStat, 0.5); } From bf7e222d4d9050ea8dc1aba169f12671a008f716 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 14 Jan 2019 11:15:12 +0100 Subject: [PATCH 0227/1066] list: clear drag feedback when drop is rejected fixes #66476 --- src/vs/base/browser/ui/list/listView.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 481fa107067..4ea4a78061c 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -661,6 +661,8 @@ export class ListView implements ISpliceable, IDisposable { const canDrop = typeof result === 'boolean' ? result : result.accept; if (!canDrop) { + this.currentDragFeedback = undefined; + this.currentDragFeedbackDisposable.dispose(); return false; } From 64e95a886fc77c1029d7a5ec8768326a35058768 Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 14 Jan 2019 14:47:21 +0100 Subject: [PATCH 0228/1066] Global new file and new folder actions should be scoped by the explorer's focused item fixes #66475 --- .../files/electron-browser/fileActions.ts | 18 ++++++++++-------- .../electron-browser/views/explorerView.ts | 8 ++++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 95c5873db29..918c4f35be5 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -103,7 +103,7 @@ export class NewFileAction extends BaseErrorReportingAction { static readonly LABEL = nls.localize('createNewFile', "New File"); constructor( - private element: ExplorerItem, + private getElement: () => ExplorerItem, @INotificationService notificationService: INotificationService, @IExplorerService private explorerService: IExplorerService, @IFileService private fileService: IFileService, @@ -115,8 +115,9 @@ export class NewFileAction extends BaseErrorReportingAction { run(): Promise { let folder: ExplorerItem; - if (this.element) { - folder = this.element.isDirectory ? this.element : this.element.parent; + const element = this.getElement(); + if (element) { + folder = element.isDirectory ? element : element.parent; } else { folder = this.explorerService.roots[0]; } @@ -157,7 +158,7 @@ export class NewFolderAction extends BaseErrorReportingAction { static readonly LABEL = nls.localize('createNewFolder', "New Folder"); constructor( - private element: ExplorerItem, + private getElement: () => ExplorerItem, @INotificationService notificationService: INotificationService, @IFileService private fileService: IFileService, @IExplorerService private explorerService: IExplorerService @@ -168,8 +169,9 @@ export class NewFolderAction extends BaseErrorReportingAction { run(): Promise { let folder: ExplorerItem; - if (this.element) { - folder = this.element.isDirectory ? this.element : this.element.parent; + const element = this.getElement(); + if (element) { + folder = element.isDirectory ? element : element.parent; } else { folder = this.explorerService.roots[0]; } @@ -1040,7 +1042,7 @@ function getContext(listWidget: ListWidget): IExplorerContext { // TODO@isidor these commands are calling into actions due to the complex inheritance action structure. // It should be the other way around, that actions call into commands. -function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature1): Promise { +function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: IConstructorSignature1<() => ExplorerItem, Action>): Promise { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); const viewletService = accessor.get(IViewletService); @@ -1055,7 +1057,7 @@ function openExplorerAndRunAction(accessor: ServicesAccessor, constructor: ICons if (explorerView && explorerView.isBodyVisible()) { explorerView.focus(); const { stat } = getContext(listService.lastFocusedList); - const action = instantationService.createInstance(constructor, stat); + const action = instantationService.createInstance(constructor, () => stat); return action.run(); } 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 492c178177d..c710b6b01fb 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -209,8 +209,12 @@ export class ExplorerView extends ViewletPanel { getActions(): IAction[] { const actions: Action[] = []; - actions.push(this.instantiationService.createInstance(NewFileAction, undefined)); - actions.push(this.instantiationService.createInstance(NewFolderAction, undefined)); + const getFocus = () => { + const focus = this.tree.getFocus(); + return focus.length > 0 ? focus[0] : undefined; + }; + actions.push(this.instantiationService.createInstance(NewFileAction, getFocus)); + actions.push(this.instantiationService.createInstance(NewFolderAction, getFocus)); actions.push(this.instantiationService.createInstance(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL)); actions.push(this.instantiationService.createInstance(CollapseAction2, this.tree, true, 'explorer-action collapse-explorer')); From 68fa8e78354bb94741ea7583c056bd8187fe6a9b Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 14 Jan 2019 14:54:33 +0100 Subject: [PATCH 0229/1066] fixes #66477 --- .../parts/files/electron-browser/views/explorerViewer.ts | 5 +++++ 1 file changed, 5 insertions(+) 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 b06374ab048..d377ac121c8 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -453,6 +453,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const items = (data as ElementsDragAndDropData).elements; if (!target) { + // Droping onto the empty area. Do not accept if items dragged are already children of the root + if (items.every(i => i.parent.isRoot)) { + return false; + } + return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; } From 8d7303fbddf880b0ce83557d54336548fcf6f9a0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 14 Jan 2019 15:34:45 +0100 Subject: [PATCH 0230/1066] list: should not fwd drop when customer says no drop --- src/vs/base/browser/ui/list/listView.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 4ea4a78061c..6b77d619d40 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -173,6 +173,7 @@ export class ListView implements ISpliceable, IDisposable { private supportDynamicHeights: boolean; private dnd: IListViewDragAndDrop; + private canDrop: boolean = false; private currentDragData: IDragAndDropData | undefined; private currentDragFeedback: number[] | undefined; private currentDragFeedbackDisposable: IDisposable = Disposable.None; @@ -658,9 +659,9 @@ export class ListView implements ISpliceable, IDisposable { } const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent); - const canDrop = typeof result === 'boolean' ? result : result.accept; + this.canDrop = typeof result === 'boolean' ? result : result.accept; - if (!canDrop) { + if (!this.canDrop) { this.currentDragFeedback = undefined; this.currentDragFeedbackDisposable.dispose(); return false; @@ -729,6 +730,10 @@ export class ListView implements ISpliceable, IDisposable { } private onDrop(event: IListDragEvent): void { + if (!this.canDrop) { + return; + } + const dragData = this.currentDragData; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); @@ -745,6 +750,7 @@ export class ListView implements ISpliceable, IDisposable { } private onDragEnd(): void { + this.canDrop = false; this.teardownDragAndDropScrollTopAnimation(); this.clearDragOverFeedback(); this.currentDragData = undefined; From 8c4af4953c284851230d219709cbf6bc2103c8ed Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 14 Jan 2019 16:49:53 +0100 Subject: [PATCH 0231/1066] fixes #66487 --- .../parts/files/electron-browser/views/explorerViewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d377ac121c8..ffdd6257b14 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -495,7 +495,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // All (target = model) if (!target) { - return this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE ? { accept: true, bubble: TreeDragOverBubble.Down } : false; // can only drop folders to workspace + return { accept: true, bubble: TreeDragOverBubble.Down }; } // All (target = file/folder) From 57d59ad535c57f5a598f81ebd5ab61bd96505412 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 14 Jan 2019 17:11:33 +0100 Subject: [PATCH 0232/1066] #66397 Use single identifier for local extension which is gallery one --- src/vs/code/node/cliProcessMain.ts | 8 +- .../common/extensionEnablementService.ts | 22 +- .../common/extensionManagementUtil.ts | 25 +- .../node/extensionLifecycle.ts | 12 +- .../node/extensionManagementService.ts | 235 +++++++++--------- .../node/extensionManagementUtil.ts | 16 -- .../extensionEnablementService.test.ts | 34 +-- .../platform/extensions/common/extensions.ts | 21 +- .../localizations/node/localizations.ts | 7 +- .../experimentService.test.ts | 5 +- .../electron-browser/extensionTipsService.ts | 2 +- .../electron-browser/extensionsActions.ts | 2 +- .../electron-browser/extensionsUtils.ts | 17 +- .../electron-browser/extensionsViewlet.ts | 4 +- .../electron-browser/extensionsViews.ts | 4 +- .../node/extensionsWorkbenchService.ts | 73 +++--- .../extensionsActions.test.ts | 19 +- .../electron-browser/extensionsViews.test.ts | 15 +- .../extensionsWorkbenchService.test.ts | 7 +- .../electron-browser/preferencesSearch.ts | 6 +- .../page/electron-browser/welcomePage.ts | 14 +- .../electron-browser/extensionService.ts | 2 +- .../inactiveExtensionUrlHandler.ts | 2 +- .../extensions/node/extensionPoints.ts | 23 +- .../electron-browser/workbenchIssueService.ts | 2 +- 25 files changed, 252 insertions(+), 325 deletions(-) diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index fd186de133c..2bc020d9a28 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -153,7 +153,7 @@ class Main { return Promise.reject(new Error(`${notFound(version ? `${id}@${version}` : id)}\n${useId}`)); } - const [installedExtension] = installed.filter(e => areSameExtensions(e.galleryIdentifier, { id })); + const [installedExtension] = installed.filter(e => areSameExtensions(e.identifier, { id })); if (installedExtension) { if (extension.version !== installedExtension.manifest.version) { if (version || force) { @@ -186,10 +186,10 @@ class Main { const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; const installedExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User); - const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.galleryIdentifier) && semver.gt(local.manifest.version, manifest.version))[0]; + const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.identifier) && semver.gt(local.manifest.version, manifest.version))[0]; if (newer && !force) { - console.log(localize('forceDowngrade', "A newer version of this extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", newer.galleryIdentifier.id, newer.manifest.version, manifest.version)); + console.log(localize('forceDowngrade', "A newer version of this extension '{0}' v{1} is already installed. Use '--force' option to downgrade to older version.", newer.identifier.id, newer.manifest.version, manifest.version)); return false; } @@ -225,7 +225,7 @@ class Main { return sequence(extensions.map(extension => () => { return getExtensionId(extension).then(id => { return this.extensionManagementService.getInstalled(ExtensionType.User).then(installed => { - const [extension] = installed.filter(e => areSameExtensions(e.galleryIdentifier, { id })); + const [extension] = installed.filter(e => areSameExtensions(e.identifier, { id })); if (!extension) { return Promise.reject(new Error(`${notInstalled(id)}\n${useId}`)); diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 4f8c849352c..3de0a1dbaf5 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, isIExtensionIdentifier, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getIdFromLocalExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -67,8 +67,8 @@ export class ExtensionEnablementService extends Disposable implements IExtension const allInstalledExtensions = await this.extensionManagementService.getInstalled(); for (const installedExtension of allInstalledExtensions) { if (this._isExtensionDisabledInEnvironment(installedExtension)) { - if (!result.some(r => areSameExtensions(r, installedExtension.galleryIdentifier))) { - result.push(installedExtension.galleryIdentifier); + if (!result.some(r => areSameExtensions(r, installedExtension.identifier))) { + result.push(installedExtension.identifier); } } } @@ -81,7 +81,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension if (this._isExtensionDisabledInEnvironment(extension)) { return EnablementState.Disabled; } - const identifier = extension.galleryIdentifier; + const identifier = extension.identifier; if (this.hasWorkspace) { if (this._getEnabledExtensions(StorageScope.WORKSPACE).filter(e => areSameExtensions(e, identifier))[0]) { return EnablementState.WorkspaceEnabled; @@ -115,7 +115,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension if (!this.canChangeEnablement(arg)) { return Promise.resolve(false); } - identifier = arg.galleryIdentifier; + identifier = arg.identifier; } const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled; @@ -160,7 +160,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension } const disabledExtensions = this.environmentService.disableExtensions; if (Array.isArray(disabledExtensions)) { - return disabledExtensions.some(id => areSameExtensions({ id }, extension.galleryIdentifier)); + return disabledExtensions.some(id => areSameExtensions({ id }, extension.identifier)); } return false; } @@ -297,20 +297,16 @@ export class ExtensionEnablementService extends Disposable implements IExtension private _onDidInstallExtension(event: DidInstallExtensionEvent): void { if (event.local && event.operation === InstallOperation.Install) { const wasDisabled = !this.isEnabled(event.local); - this._reset(event.local.galleryIdentifier); + this._reset(event.local.identifier); if (wasDisabled) { - this._onEnablementChanged.fire(event.local.galleryIdentifier); + this._onEnablementChanged.fire(event.local.identifier); } } } private _onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void { if (!error) { - const id = getIdFromLocalExtensionId(identifier.id); - if (id) { - const extension = { id, uuid: identifier.uuid }; - this._reset(extension); - } + this._reset(identifier); } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 238ebbe3f9b..1099a0be9ba 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -5,7 +5,6 @@ import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { compareIgnoreCase } from 'vs/base/common/strings'; -import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; export function areSameExtensions(a: IExtensionIdentifier, b: IExtensionIdentifier): boolean { if (a.uuid && b.uuid) { @@ -25,28 +24,6 @@ export function getGalleryExtensionId(publisher: string, name: string): string { return `${publisher.toLocaleLowerCase()}.${name.toLocaleLowerCase()}`; } -export const LOCAL_EXTENSION_ID_REGEX = /^([^.]+\..+)-(\d+\.\d+\.\d+(-.*)?)$/; - -export function getIdFromLocalExtensionId(localExtensionId: string): string { - const matches = LOCAL_EXTENSION_ID_REGEX.exec(localExtensionId); - if (matches && matches[1]) { - return adoptToGalleryExtensionId(matches[1]); - } - return adoptToGalleryExtensionId(localExtensionId); -} - -export function getLocalExtensionIdFromGallery(extension: IGalleryExtension, version: string): string { - return getLocalExtensionId(extension.identifier.id, version); -} - -export function getLocalExtensionIdFromManifest(manifest: IExtensionManifest): string { - return getLocalExtensionId(getGalleryExtensionId(manifest.publisher, manifest.name), manifest.version); -} - -export function getLocalExtensionId(id: string, version: string): string { - return `${id}-${version}`; -} - export function groupByExtension(extensions: T[], getExtensionIdentifier: (t: T) => IExtensionIdentifier): T[][] { const byExtension: T[][] = []; const findGroup = extension => { @@ -70,7 +47,7 @@ export function groupByExtension(extensions: T[], getExtensionIdentifier: (t: export function getLocalExtensionTelemetryData(extension: ILocalExtension): any { return { - id: extension.galleryIdentifier.id, + id: extension.identifier.id, name: extension.manifest.name, galleryId: null, publisherId: extension.metadata ? extension.metadata.publisherId : null, diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 9b81e959837..5d98b943253 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -29,10 +29,10 @@ export class ExtensionsLifecycle extends Disposable { async postUninstall(extension: ILocalExtension): Promise { const script = this.parseScript(extension, 'uninstall'); if (script) { - this.logService.info(extension.identifier.id, `Running post uninstall script`); + this.logService.info(extension.identifier.id, extension.manifest.version, `Running post uninstall script`); await this.processesLimiter.queue(() => this.runLifecycleHook(script.script, 'uninstall', script.args, true, extension) - .then(() => this.logService.info(extension.identifier.id, `Finished running post uninstall script`), err => this.logService.error(extension.identifier.id, `Failed to run post uninstall script: ${err}`))); + .then(() => this.logService.info(extension.identifier.id, extension.manifest.version, `Finished running post uninstall script`), err => this.logService.error(extension.identifier.id, extension.manifest.version, `Failed to run post uninstall script: ${err}`))); } return rimraf(this.getExtensionStoragePath(extension)).then(undefined, e => this.logService.error('Error while removing extension storage path', e)); } @@ -42,7 +42,7 @@ export class ExtensionsLifecycle extends Disposable { if (extension.location.scheme === Schemas.file && extension.manifest && extension.manifest['scripts'] && typeof extension.manifest['scripts'][scriptKey] === 'string') { const script = (extension.manifest['scripts'][scriptKey]).split(' '); if (script.length < 2 || script[0] !== 'node' || !script[1]) { - this.logService.warn(extension.identifier.id, `${scriptKey} should be a node script`); + this.logService.warn(extension.identifier.id, extension.manifest.version, `${scriptKey} should be a node script`); return null; } return { script: posix.join(extension.location.fsPath, script[1]), args: script.slice(2) || [] }; @@ -105,8 +105,8 @@ export class ExtensionsLifecycle extends Disposable { const onStderr = Event.fromNodeEventEmitter(extensionUninstallProcess.stderr, 'data'); // Log output - onStdout(data => this.logService.info(extension.identifier.id, `post-${lifecycleType}`, data)); - onStderr(data => this.logService.error(extension.identifier.id, `post-${lifecycleType}`, data)); + onStdout(data => this.logService.info(extension.identifier.id, extension.manifest.version, `post-${lifecycleType}`, data)); + onStderr(data => this.logService.error(extension.identifier.id, extension.manifest.version, `post-${lifecycleType}`, data)); const onOutput = Event.any( Event.map(onStdout, o => ({ data: `%c${o}`, format: [''] })), @@ -130,6 +130,6 @@ export class ExtensionsLifecycle extends Disposable { } private getExtensionStoragePath(extension: ILocalExtension): string { - return posix.join(this.environmentService.globalStorageHome, extension.galleryIdentifier.id.toLowerCase()); + return posix.join(this.environmentService.globalStorageHome, extension.identifier.id.toLowerCase()); } } diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 3e5ea6037e8..4d8a6db6c3c 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -21,7 +21,7 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getLocalExtensionId, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, getIdFromLocalExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +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'; @@ -44,7 +44,7 @@ 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 } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -95,7 +95,7 @@ function readManifest(extensionPath: string): Promise<{ manifest: IExtensionMani interface InstallableExtension { zipPath: string; - id: string; + identifierWithVersion: ExtensionIdentifierWithVersion; metadata: IGalleryMetadata | null; } @@ -150,7 +150,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } zip(extension: ILocalExtension): Promise { - this.logService.trace('ExtensionManagementService#zip', extension.galleryIdentifier.id); + this.logService.trace('ExtensionManagementService#zip', extension.identifier.id); return this.collectFiles(extension) .then(files => zip(path.join(tmpdir(), generateUuid()), files)) .then(path => URI.file(path)); @@ -195,34 +195,39 @@ export class ExtensionManagementService extends Disposable implements IExtension return getManifest(zipPath) .then(manifest => { - const identifier = { id: getLocalExtensionIdFromManifest(manifest) }; + const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; + let operation: InstallOperation = InstallOperation.Install; if (manifest.engines && manifest.engines.vscode && !isEngineValid(manifest.engines.vscode)) { return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", identifier.id, pkg.version))); } - return this.removeIfExists(identifier.id).then( - () => { - const extensionIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; - return this.getInstalled(ExtensionType.User) - .then(installedExtensions => { - const newer = installedExtensions.filter(local => areSameExtensions(extensionIdentifier, local.galleryIdentifier) && semver.gt(local.manifest.version, manifest.version))[0]; - return newer ? this.uninstall(newer, true) : undefined; - }) - .then(() => { - this.logService.info('Installing the extension:', identifier.id); - this._onInstallExtension.fire({ identifier, zipPath }); - return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) - .then( - metadata => this.installFromZipPath(identifier, zipPath, metadata, type, token), - error => this.installFromZipPath(identifier, zipPath, null, type, token)) - .then( - () => { this.logService.info('Successfully installed the extension:', identifier.id); return identifier; }, - e => { - this.logService.error('Failed to install the extension:', identifier.id, e.message); - return Promise.reject(e); - }); - }); - }, - e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + const identifierWithVersion = new ExtensionIdentifierWithVersion(identifier, manifest.version); + return this.getInstalled(ExtensionType.User) + .then(installedExtensions => { + const existing = installedExtensions.filter(i => areSameExtensions(identifier, i.identifier))[0]; + if (existing) { + operation = InstallOperation.Update; + if (identifierWithVersion.equals(new ExtensionIdentifierWithVersion(existing.identifier, existing.manifest.version))) { + return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart VS Code before reinstalling {0}.", manifest.displayName || manifest.name)))); + } else if (semver.gt(existing.manifest.version, manifest.version)) { + return this.uninstall(existing, true); + } + } + return undefined; + }) + .then(() => { + this.logService.info('Installing the extension:', identifier.id); + this._onInstallExtension.fire({ identifier, zipPath }); + return this.getMetadata(getGalleryExtensionId(manifest.publisher, manifest.name)) + .then( + metadata => this.installFromZipPath(identifierWithVersion, zipPath, metadata, type, operation, token), + () => this.installFromZipPath(identifierWithVersion, zipPath, null, type, operation, token)) + .then( + () => { this.logService.info('Successfully installed the extension:', identifier.id); return identifier; }, + e => { + this.logService.error('Failed to install the extension:', identifier.id, e.message); + return Promise.reject(e); + }); + }); }); }); }); @@ -239,23 +244,13 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.downloadService.download(vsix, downloadedLocation).then(() => URI.file(downloadedLocation)); } - private removeIfExists(id: string): Promise { - return this.getInstalled(ExtensionType.User) - .then(installed => installed.filter(i => i.identifier.id === id)[0]) - .then(existing => existing ? this.removeExtension(existing, 'existing') : undefined); - } - - private installFromZipPath(identifier: IExtensionIdentifier, zipPath: string, metadata: IGalleryMetadata | null, type: ExtensionType, token: CancellationToken): Promise { - return this.toNonCancellablePromise(this.getInstalled() - .then(installed => { - const operation = this.getOperation({ id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }, installed); - return this.installExtension({ zipPath, id: identifier.id, metadata }, type, token) - .then(local => this.installDependenciesAndPackExtensions(local, null).then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) - .then( - local => { this._onDidInstallExtension.fire({ identifier, zipPath, local, operation }); return local; }, - error => { this._onDidInstallExtension.fire({ identifier, zipPath, operation, error }); return Promise.reject(error); } - ); - })); + private installFromZipPath(identifierWithVersion: ExtensionIdentifierWithVersion, zipPath: string, metadata: IGalleryMetadata | null, type: ExtensionType, operation: InstallOperation, token: CancellationToken): Promise { + return this.toNonCancellablePromise(this.installExtension({ zipPath, identifierWithVersion, metadata }, type, token) + .then(local => this.installDependenciesAndPackExtensions(local, null).then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) + .then( + local => { this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, local, operation }); return local; }, + error => { this._onDidInstallExtension.fire({ identifier: identifierWithVersion.identifier, zipPath, operation, error }); return Promise.reject(error); } + )); } async installFromGallery(extension: IGalleryExtension): Promise { @@ -266,7 +261,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const onDidInstallExtensionSuccess = (extension: IGalleryExtension, operation: InstallOperation, local: ILocalExtension) => { this.logService.info(`Extensions installed successfully:`, extension.identifier.id); - this._onDidInstallExtension.fire({ identifier: { id: getLocalExtensionIdFromGallery(extension, extension.version), uuid: extension.identifier.uuid }, gallery: extension, local, operation }); + this._onDidInstallExtension.fire({ identifier: extension.identifier, gallery: extension, local, operation }); this.reportTelemetry(this.getTelemetryEvent(operation), getGalleryExtensionTelemetryData(extension), new Date().getTime() - startTime, undefined); }; @@ -287,18 +282,17 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.reject(error); } - const key = getLocalExtensionId(extension.identifier.id, extension.version); + const key = new ExtensionIdentifierWithVersion(extension.identifier, extension.version).key(); let cancellablePromise = this.installingExtensions.get(key); if (!cancellablePromise) { - let operation: InstallOperation = InstallOperation.Install; let cancellationToken: CancellationToken, successCallback: (a?: any) => void, errorCallback: (e?: any) => any | null; cancellablePromise = createCancelablePromise(token => { cancellationToken = token; return new Promise((c, e) => { successCallback = c; errorCallback = e; }); }); this.installingExtensions.set(key, cancellablePromise); try { const installed = await this.getInstalled(ExtensionType.User); - const existingExtension = installed.filter(i => areSameExtensions(i.galleryIdentifier, extension.identifier))[0]; + const existingExtension = installed.filter(i => areSameExtensions(i.identifier, extension.identifier))[0]; if (existingExtension) { operation = InstallOperation.Update; if (semver.gt(existingExtension.manifest.version, extension.version)) { @@ -349,7 +343,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } reinstallFromGallery(extension: ILocalExtension): Promise { - this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.galleryIdentifier.id); + this.logService.trace('ExtensionManagementService#reinstallFromGallery', extension.identifier.id); if (!this.galleryService.isEnabled()) { return Promise.reject(new Error(nls.localize('MarketPlaceDisabled', "Marketplace is not enabled"))); } @@ -366,10 +360,6 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private getOperation(extensionToInstall: IExtensionIdentifier, installed: ILocalExtension[]): InstallOperation { - return installed.some(i => areSameExtensions(i.galleryIdentifier, extensionToInstall)) ? InstallOperation.Update : InstallOperation.Install; - } - private getTelemetryEvent(operation: InstallOperation): string { return operation === InstallOperation.Update ? 'extensionGallery:update' : 'extensionGallery:install'; } @@ -393,7 +383,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.info('Downloaded extension:', extension.identifier.id, zipPath); return getManifest(zipPath) .then( - manifest => ({ zipPath, id: getLocalExtensionIdFromManifest(manifest), metadata }), + manifest => ({ zipPath, identifierWithVersion: new ExtensionIdentifierWithVersion(extension.identifier, manifest.version), metadata }), error => Promise.reject(new ExtensionManagementError(this.joinErrors(error).message, INSTALL_ERROR_VALIDATING)) ); }, @@ -401,7 +391,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private installExtension(installableExtension: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { - return this.unsetUninstalledAndGetLocal(installableExtension.id) + return this.unsetUninstalledAndGetLocal(installableExtension.identifierWithVersion) .then( local => { if (local) { @@ -417,35 +407,37 @@ export class ExtensionManagementService extends Disposable implements IExtension }); } - private unsetUninstalledAndGetLocal(id: string): Promise { - return this.isUninstalled(id) + private unsetUninstalledAndGetLocal(identifierWithVersion: ExtensionIdentifierWithVersion): Promise { + return this.isUninstalled(identifierWithVersion) .then(isUninstalled => { if (isUninstalled) { - this.logService.trace('Removing the extension from uninstalled list:', id); + this.logService.trace('Removing the extension from uninstalled list:', identifierWithVersion.identifier.id); // If the same version of extension is marked as uninstalled, remove it from there and return the local. - return this.unsetUninstalled(id) + return this.unsetUninstalled(identifierWithVersion) .then(() => { - this.logService.info('Removed the extension from uninstalled list:', id); + this.logService.info('Removed the extension from uninstalled list:', identifierWithVersion.identifier.id); return this.getInstalled(ExtensionType.User); }) - .then(installed => installed.filter(i => i.identifier.id === id)[0]); + .then(installed => installed.filter(i => new ExtensionIdentifierWithVersion(i.identifier, i.manifest.version).equals(identifierWithVersion))[0]); } return null; }); } - private extractAndInstall({ zipPath, id, metadata }: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { + private extractAndInstall({ zipPath, identifierWithVersion, metadata }: InstallableExtension, type: ExtensionType, token: CancellationToken): Promise { + const { identifier } = identifierWithVersion; const location = type === ExtensionType.User ? this.extensionsPath : this.systemExtensionsPath; - const tempPath = path.join(location, `.${id}`); - const extensionPath = path.join(location, id); + const folderName = identifierWithVersion.key(); + const tempPath = path.join(location, `.${folderName}`); + const extensionPath = path.join(location, folderName); return pfs.rimraf(extensionPath) - .then(() => this.extractAndRename(id, zipPath, tempPath, extensionPath, token), e => Promise.reject(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, id), INSTALL_ERROR_DELETING))) - .then(() => this.scanExtension(id, location, type)) + .then(() => this.extractAndRename(identifier, zipPath, tempPath, extensionPath, token), e => Promise.reject(new ExtensionManagementError(nls.localize('errorDeleting', "Unable to delete the existing folder '{0}' while installing the extension '{1}'. Please delete the folder manually and try again", extensionPath, identifier.id), INSTALL_ERROR_DELETING))) + .then(() => this.scanExtension(folderName, location, type)) .then(local => { if (!local) { return Promise.reject(nls.localize('cannot read', "Cannot read the extension from {0}", location)); } - this.logService.info('Installation completed.', id); + this.logService.info('Installation completed.', identifier.id); if (metadata) { local.metadata = metadata; return this.saveMetadataForLocalExtension(local); @@ -454,9 +446,9 @@ export class ExtensionManagementService extends Disposable implements IExtension }, error => pfs.rimraf(extensionPath).then(() => Promise.reject(error), () => Promise.reject(error))); } - private extractAndRename(id: string, zipPath: string, extractPath: string, renamePath: string, token: CancellationToken): Promise { - return this.extract(id, zipPath, extractPath, token) - .then(() => this.rename(id, extractPath, renamePath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */) + private extractAndRename(identifier: IExtensionIdentifier, zipPath: string, extractPath: string, renamePath: string, token: CancellationToken): Promise { + return this.extract(identifier, zipPath, extractPath, token) + .then(() => this.rename(identifier, extractPath, renamePath, Date.now() + (2 * 60 * 1000) /* Retry for 2 minutes */) .then( () => this.logService.info('Renamed to', renamePath), e => { @@ -465,24 +457,24 @@ export class ExtensionManagementService extends Disposable implements IExtension })); } - private extract(id: string, zipPath: string, extractPath: string, token: CancellationToken): Promise { + private extract(identifier: IExtensionIdentifier, zipPath: string, extractPath: string, token: CancellationToken): Promise { this.logService.trace(`Started extracting the extension from ${zipPath} to ${extractPath}`); return pfs.rimraf(extractPath) .then( () => extract(zipPath, extractPath, { sourcePath: 'extension', overwrite: true }, this.logService, token) .then( - () => this.logService.info(`Extracted extension to ${extractPath}:`, id), + () => this.logService.info(`Extracted extension to ${extractPath}:`, identifier.id), e => always(pfs.rimraf(extractPath), () => 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))); } - private rename(id: string, extractPath: string, renamePath: string, retryUntil: number): Promise { + private rename(identfier: IExtensionIdentifier, extractPath: string, renamePath: string, retryUntil: number): Promise { return pfs.rename(extractPath, renamePath) .then(undefined, error => { if (isWindows && error && error.code === 'EPERM' && Date.now() < retryUntil) { - this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`); - return this.rename(id, extractPath, renamePath, retryUntil); + this.logService.info(`Failed renaming ${extractPath} to ${renamePath} with 'EPERM' error. Trying again...`, identfier.id); + return this.rename(identfier, extractPath, renamePath, retryUntil); } return Promise.reject(new ExtensionManagementError(error.message || nls.localize('renameError', "Unknown error while renaming {0} to {1}", extractPath, renamePath), error.code || INSTALL_ERROR_RENAMING)); }); @@ -504,8 +496,8 @@ export class ExtensionManagementService extends Disposable implements IExtension if (dependenciesAndPackExtensions.length) { return this.getInstalled() .then(installed => { - // filter out installing and installed extensions - const names = dependenciesAndPackExtensions.filter(id => !this.installingExtensions.has(adoptToGalleryExtensionId(id)) && installed.every(({ galleryIdentifier }) => !areSameExtensions(galleryIdentifier, { id }))); + // filter out installed extensions + const names = dependenciesAndPackExtensions.filter(id => installed.every(({ identifier: galleryIdentifier }) => !areSameExtensions(galleryIdentifier, { id }))); if (names.length) { return this.galleryService.query({ names, pageSize: dependenciesAndPackExtensions.length }) .then(galleryResult => { @@ -524,20 +516,18 @@ export class ExtensionManagementService extends Disposable implements IExtension private rollback(extensions: IGalleryExtension[]): Promise { return this.getInstalled(ExtensionType.User) .then(installed => - Promise.all(installed.filter(local => extensions.some(galleryExtension => local.identifier.id === getLocalExtensionIdFromGallery(galleryExtension, galleryExtension.version))) // Only check id (pub.name-version) because we want to rollback the exact version + Promise.all(installed.filter(local => extensions.some(galleryExtension => new ExtensionIdentifierWithVersion(local.identifier, local.manifest.version).equals(new ExtensionIdentifierWithVersion(galleryExtension.identifier, galleryExtension.version)))) // Check with version because we want to rollback the exact version .map(local => this.uninstall(local, true)))) .then(() => undefined, () => undefined); } uninstall(extension: ILocalExtension, force = false): Promise { - this.logService.trace('ExtensionManagementService#uninstall', extension.galleryIdentifier.id); + this.logService.trace('ExtensionManagementService#uninstall', extension.identifier.id); return this.toNonCancellablePromise(this.getInstalled(ExtensionType.User) .then(installed => { - const extensionsToUninstall = installed - .filter(e => e.manifest.publisher === extension.manifest.publisher && e.manifest.name === extension.manifest.name); - if (extensionsToUninstall.length) { - const promises = extensionsToUninstall.map(e => this.checkForDependenciesAndUninstall(e, installed)); - return Promise.all(promises).then(() => null, error => Promise.reject(this.joinErrors(error))); + const extensionToUninstall = installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; + if (extensionToUninstall) { + return this.checkForDependenciesAndUninstall(extensionToUninstall, installed).then(() => null, error => Promise.reject(this.joinErrors(error))); } else { return Promise.reject(new Error(nls.localize('notInstalled', "Extension '{0}' is not installed.", extension.manifest.displayName || extension.manifest.name))); } @@ -545,7 +535,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata): Promise { - this.logService.trace('ExtensionManagementService#updateMetadata', local.galleryIdentifier.id); + this.logService.trace('ExtensionManagementService#updateMetadata', local.identifier.id); local.metadata = metadata; return this.saveMetadataForLocalExtension(local) .then(localExtension => { @@ -558,7 +548,7 @@ export class ExtensionManagementService extends Disposable implements IExtension if (!local.metadata) { return Promise.resolve(local); } - const manifestPath = path.join(this.extensionsPath, local.identifier.id, 'package.json'); + const manifestPath = path.join(local.location.fsPath, 'package.json'); return pfs.readFile(manifestPath, 'utf8') .then(raw => parseManifest(raw)) .then(({ manifest }) => assign(manifest, { __metadata: local.metadata })) @@ -572,11 +562,11 @@ export class ExtensionManagementService extends Disposable implements IExtension } private findGalleryExtension(local: ILocalExtension): Promise { - if (local.galleryIdentifier.uuid) { - return this.findGalleryExtensionById(local.galleryIdentifier.uuid) - .then(galleryExtension => galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.galleryIdentifier.id)); + if (local.identifier.uuid) { + return this.findGalleryExtensionById(local.identifier.uuid) + .then(galleryExtension => galleryExtension ? galleryExtension : this.findGalleryExtensionByName(local.identifier.id)); } - return this.findGalleryExtensionByName(local.galleryIdentifier.id); + return this.findGalleryExtensionByName(local.identifier.id); } private findGalleryExtensionById(uuid: string): Promise { @@ -644,7 +634,7 @@ export class ExtensionManagementService extends Disposable implements IExtension checked.push(extension); const extensionsPack = extension.manifest.extensionPack ? extension.manifest.extensionPack : []; if (extensionsPack.length) { - const packedExtensions = installed.filter(i => extensionsPack.some(id => areSameExtensions({ id }, i.galleryIdentifier))); + const packedExtensions = installed.filter(i => extensionsPack.some(id => areSameExtensions({ id }, i.identifier))); const packOfPackedExtensions: ILocalExtension[] = []; for (const packedExtension of packedExtensions) { packOfPackedExtensions.push(...this.getAllPackExtensionsToUninstall(packedExtension, installed, checked)); @@ -655,7 +645,7 @@ export class ExtensionManagementService extends Disposable implements IExtension } private getDependents(extension: ILocalExtension, installed: ILocalExtension[]): ILocalExtension[] { - return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.galleryIdentifier))); + return installed.filter(e => e.manifest.extensionDependencies && e.manifest.extensionDependencies.some(id => areSameExtensions({ id }, extension.identifier))); } private doUninstall(extension: ILocalExtension): Promise { @@ -672,30 +662,30 @@ export class ExtensionManagementService extends Disposable implements IExtension return Promise.resolve(pfs.exists(extension.location.fsPath)) .then(exists => exists ? null : Promise.reject(new Error(nls.localize('notExists', "Could not find extension")))) .then(() => { - this.logService.info('Uninstalling extension:', extension.galleryIdentifier.id); + this.logService.info('Uninstalling extension:', extension.identifier.id); this._onUninstallExtension.fire(extension.identifier); }); } private uninstallExtension(local: ILocalExtension): Promise { - let promise = this.uninstallingExtensions.get(local.galleryIdentifier.id); + let promise = this.uninstallingExtensions.get(local.identifier.id); if (!promise) { // Set all versions of the extension as uninstalled promise = createCancelablePromise(token => this.scanUserExtensions(false) - .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.galleryIdentifier, local.galleryIdentifier)))) - .then(() => { this.uninstallingExtensions.delete(local.galleryIdentifier.id); })); - this.uninstallingExtensions.set(local.galleryIdentifier.id, promise); + .then(userExtensions => this.setUninstalled(...userExtensions.filter(u => areSameExtensions(u.identifier, local.identifier)))) + .then(() => { this.uninstallingExtensions.delete(local.identifier.id); })); + this.uninstallingExtensions.set(local.identifier.id, promise); } return promise; } private async postUninstallExtension(extension: ILocalExtension, error?: Error): Promise { if (error) { - this.logService.error('Failed to uninstall extension:', extension.galleryIdentifier.id, error.message); + this.logService.error('Failed to uninstall extension:', extension.identifier.id, error.message); } else { - this.logService.info('Successfully uninstalled extension:', extension.galleryIdentifier.id); + this.logService.info('Successfully uninstalled extension:', extension.identifier.id); // only report if extension has a mapped gallery extension. UUID identifies the gallery extension. - if (extension.galleryIdentifier.uuid) { + if (extension.identifier.uuid) { await this.galleryService.reportStatistic(extension.manifest.publisher, extension.manifest.name, extension.manifest.version, StatisticType.Uninstall); } } @@ -736,7 +726,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.scanExtensions(this.devSystemExtensionsPath, ExtensionType.System) .then(result => { this.logService.info('Scanned dev system extensions:', result.length); - return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.galleryIdentifier, { id }))); + return result.filter(r => devSystemExtensionsList.some(id => areSameExtensions(r.identifier, { id }))); }); } else { return []; @@ -750,9 +740,9 @@ export class ExtensionManagementService extends Disposable implements IExtension this.logService.trace('Started scanning user extensions'); return Promise.all([this.getUninstalledExtensions(), this.scanExtensions(this.extensionsPath, ExtensionType.User)]) .then(([uninstalled, extensions]) => { - extensions = extensions.filter(e => !uninstalled[e.identifier.id]); + extensions = extensions.filter(e => !uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); if (excludeOutdated) { - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.galleryIdentifier); + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); extensions = byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version))[0]); } this.logService.info('Scanned user extensions:', extensions.length); @@ -779,9 +769,8 @@ export class ExtensionManagementService extends Disposable implements IExtension const readmeUrl = readme ? URI.file(path.join(extensionPath, readme)) : null; const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; const changelogUrl = changelog ? URI.file(path.join(extensionPath, changelog)) : null; - const identifier = { id: type === ExtensionType.System ? folderName : getLocalExtensionIdFromManifest(manifest), uuid: metadata ? metadata.id : null }; - const galleryIdentifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: identifier.uuid }; - return { type, identifier, galleryIdentifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl }; + const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: metadata ? metadata.id : null }; + return { type, identifier, manifest, metadata, location: URI.file(extensionPath), readmeUrl, changelogUrl }; })) .then(undefined, () => null); } @@ -795,7 +784,7 @@ export class ExtensionManagementService extends Disposable implements IExtension return this.getUninstalledExtensions() .then(uninstalled => this.scanExtensions(this.extensionsPath, ExtensionType.User) // All user extensions .then(extensions => { - const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[e.identifier.id]); + const toRemove: ILocalExtension[] = extensions.filter(e => uninstalled[new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version).key()]); return Promise.all(toRemove.map(e => this.extensionLifecycle.postUninstall(e).then(() => this.removeUninstalledExtension(e)))); }) ).then(() => undefined); @@ -807,7 +796,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const toRemove: ILocalExtension[] = []; // Outdated extensions - const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.galleryIdentifier); + const byExtension: ILocalExtension[][] = groupByExtension(extensions, e => e.identifier); toRemove.push(...flatten(byExtension.map(p => p.sort((a, b) => semver.rcompare(a.manifest.version, b.manifest.version)).slice(1)))); return Promise.all(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); @@ -816,25 +805,25 @@ export class ExtensionManagementService extends Disposable implements IExtension private removeUninstalledExtension(extension: ILocalExtension): Promise { return this.removeExtension(extension, 'uninstalled') - .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[extension.identifier.id])) + .then(() => this.withUninstalledExtensions(uninstalled => delete uninstalled[new ExtensionIdentifierWithVersion(extension.identifier, extension.manifest.version).key()])) .then(() => undefined); } private removeExtension(extension: ILocalExtension, type: string): Promise { - this.logService.trace(`Deleting ${type} extension from disk`, extension.galleryIdentifier.id, extension.location.fsPath); - return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.galleryIdentifier.id, extension.location.fsPath)); + this.logService.trace(`Deleting ${type} extension from disk`, extension.identifier.id, extension.location.fsPath); + return pfs.rimraf(extension.location.fsPath).then(() => this.logService.info('Deleted from disk', extension.identifier.id, extension.location.fsPath)); } - private isUninstalled(id: string): Promise { - return this.filterUninstalled(id).then(uninstalled => uninstalled.length === 1); + private isUninstalled(identfier: ExtensionIdentifierWithVersion): Promise { + return this.filterUninstalled(identfier).then(uninstalled => uninstalled.length === 1); } - private filterUninstalled(...ids: string[]): Promise { + private filterUninstalled(...identifiers: ExtensionIdentifierWithVersion[]): Promise { return this.withUninstalledExtensions(allUninstalled => { const uninstalled: string[] = []; - for (const id of ids) { - if (!!allUninstalled[id]) { - uninstalled.push(id); + for (const identifier of identifiers) { + if (!!allUninstalled[identifier.key()]) { + uninstalled.push(identifier.key()); } } return uninstalled; @@ -842,12 +831,12 @@ export class ExtensionManagementService extends Disposable implements IExtension } private setUninstalled(...extensions: ILocalExtension[]): Promise<{ [id: string]: boolean }> { - const ids = extensions.map(e => e.identifier.id); - return this.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id] = true; return result; }, {} as { [id: string]: boolean }))); + const ids: ExtensionIdentifierWithVersion[] = extensions.map(e => new ExtensionIdentifierWithVersion(e.identifier, e.manifest.version)); + return this.withUninstalledExtensions(uninstalled => assign(uninstalled, ids.reduce((result, id) => { result[id.key()] = true; return result; }, {} as { [id: string]: boolean }))); } - private unsetUninstalled(id: string): Promise { - return this.withUninstalledExtensions(uninstalled => delete uninstalled[id]); + private unsetUninstalled(extensionIdentifier: ExtensionIdentifierWithVersion): Promise { + return this.withUninstalledExtensions(uninstalled => delete uninstalled[extensionIdentifier.key()]); } private getUninstalledExtensions(): Promise<{ [id: string]: boolean; }> { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts index a0097deab0b..a837b87c821 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementUtil.ts @@ -3,26 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as semver from 'semver'; -import { adoptToGalleryExtensionId, LOCAL_EXTENSION_ID_REGEX } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { buffer } from 'vs/platform/node/zip'; import { localize } from 'vs/nls'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; -export function getIdAndVersionFromLocalExtensionId(localExtensionId: string): { id: string, version: string | null } { - const matches = LOCAL_EXTENSION_ID_REGEX.exec(localExtensionId); - if (matches && matches[1] && matches[2]) { - const version = semver.valid(matches[2]); - if (version) { - return { id: adoptToGalleryExtensionId(matches[1]), version }; - } - } - return { - id: adoptToGalleryExtensionId(localExtensionId), - version: null - }; -} - export function getManifest(vsix: string): Promise { return buffer(vsix, 'extension/package.json') .then(buffer => { diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index da5256fefa4..f10411b4bbb 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -87,7 +87,7 @@ suite('ExtensionEnablementService Test', () => { const target = sinon.spy(); testObject.onEnablementChanged(target); return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: undefined }))); + .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); }); test('test disable an extension globally again should return a falsy promise', () => { @@ -199,7 +199,7 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: undefined }))); + .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); }); test('test disable an extension globally and then for workspace', () => { @@ -220,7 +220,7 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: undefined }))); + .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); }); test('test disable an extension for workspace when there is no workspace throws error', () => { @@ -247,7 +247,7 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a', uuid: undefined }))); + .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); }); test('test enable an extension globally when already enabled return falsy promise', () => { @@ -273,7 +273,7 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceEnabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.b', uuid: undefined }))); + .then(() => assert.ok(target.calledWithExactly({ id: 'pub.b' }))); }); test('test enable an extension for workspace when already enabled return truthy promise', () => { @@ -300,7 +300,7 @@ suite('ExtensionEnablementService Test', () => { test('test installing an extension re-eanbles it when disabled globally', async () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.Disabled); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([], extensions); }); @@ -308,7 +308,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not re-eanbles it when disabled globally', async () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.Disabled); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([{ id: 'pub.a' }], extensions); }); @@ -318,11 +318,11 @@ suite('ExtensionEnablementService Test', () => { await testObject.setEnablement(local, EnablementState.Disabled); return new Promise((c, e) => { testObject.onEnablementChanged(e => { - if (e.id === local.galleryIdentifier.id) { + if (e.id === local.identifier.id) { c(); } }); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); }); }); @@ -331,14 +331,14 @@ suite('ExtensionEnablementService Test', () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.Disabled); testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); assert.ok(!target.called); }); test('test installing an extension re-eanbles it when workspace disabled', async () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([], extensions); }); @@ -346,7 +346,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not re-eanbles it when workspace disabled', async () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([{ id: 'pub.a' }], extensions); }); @@ -356,11 +356,11 @@ suite('ExtensionEnablementService Test', () => { await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); return new Promise((c, e) => { testObject.onEnablementChanged(e => { - if (e.id === local.galleryIdentifier.id) { + if (e.id === local.identifier.id) { c(); } }); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); }); }); @@ -369,7 +369,7 @@ suite('ExtensionEnablementService Test', () => { const local = aLocalExtension('pub.a'); await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Update }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); assert.ok(!target.called); }); @@ -377,14 +377,14 @@ suite('ExtensionEnablementService Test', () => { const target = sinon.spy(); const local = aLocalExtension('pub.a'); testObject.onEnablementChanged(target); - didInstallEvent.fire({ local, identifier: local.galleryIdentifier, operation: InstallOperation.Install }); + didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); assert.ok(!target.called); }); test('test remove an extension from disablement list when uninstalled', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) - .then(() => didUninstallEvent.fire({ identifier: { id: 'pub.a-1.0.0' } })) + .then(() => didUninstallEvent.fire({ identifier: { id: 'pub.a' } })) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index fe10d9a9c07..ec9209e929f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId, 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'; @@ -111,6 +111,24 @@ export interface IExtensionContributions { export type ExtensionKind = 'ui' | 'workspace'; +export class ExtensionIdentifierWithVersion { + constructor( + readonly identifier: IExtensionIdentifier, + readonly version: string + ) { } + + key(): string { + return `${this.identifier.id}-${this.version}`; + } + + equals(o: any): boolean { + if (!(o instanceof ExtensionIdentifierWithVersion)) { + return false; + } + return areSameExtensions(this.identifier, o.identifier) && this.version === o.version; + } +} + export interface IExtensionIdentifier { id: string; uuid?: string; @@ -145,7 +163,6 @@ export const enum ExtensionType { export interface IExtension { readonly type: ExtensionType; readonly identifier: IExtensionIdentifier; - readonly galleryIdentifier: IExtensionIdentifier; /** This should be removed */ readonly manifest: IExtensionManifest; readonly location: URI; } diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index 0eb9361d7db..faa6e4906d2 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -9,7 +9,7 @@ import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } fr import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Queue } from 'vs/base/common/async'; -import { areSameExtensions, getIdFromLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; import product from 'vs/platform/node/product'; @@ -68,7 +68,7 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe private onDidInstallExtension(extension: ILocalExtension | undefined): void { if (extension && extension.manifest && extension.manifest.contributes && extension.manifest.contributes.localizations && extension.manifest.contributes.localizations.length) { - this.logService.debug('Adding language packs from the extension', extension.galleryIdentifier.id); + this.logService.debug('Adding language packs from the extension', extension.identifier.id); this.update(); } } @@ -76,7 +76,6 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe private onDidUninstallExtension(identifier: IExtensionIdentifier): void { this.cache.getLanguagePacks() .then(languagePacks => { - identifier = { id: getIdFromLocalExtensionId(identifier.id), uuid: identifier.uuid }; if (Object.keys(languagePacks).some(language => languagePacks[language] && languagePacks[language].extensions.some(e => areSameExtensions(e.extensionIdentifier, identifier)))) { this.logService.debug('Removing language packs from the extension', identifier.id); this.update(); @@ -136,7 +135,7 @@ class LanguagePacksCache extends Disposable { } private createLanguagePacksFromExtension(languagePacks: { [language: string]: ILanguagePack }, extension: ILocalExtension): void { - const extensionIdentifier = extension.galleryIdentifier; + const extensionIdentifier = extension.identifier; const localizations = extension.manifest.contributes && extension.manifest.contributes.localizations ? extension.manifest.contributes.localizations : []; for (const localizationContribution of localizations) { if (extension.location.scheme === Schemas.file && isValidLocalization(localizationContribution)) { diff --git a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts index e458acc70f6..b9f252d30d1 100644 --- a/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/parts/experiments/test/electron-browser/experimentService.test.ts @@ -26,7 +26,7 @@ import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties'; -import { getLocalExtensionIdFromManifest, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; interface ExperimentSettings { @@ -46,8 +46,7 @@ function aLocalExtension(name: string = 'someext', manifest: any = {}, propertie properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getLocalExtensionIdFromManifest(manifest) }, - galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } }, properties); return Object.create({ manifest, ...properties }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index afa61cbed1d..12f655554a7 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -421,7 +421,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } this.extensionsService.getInstalled(ExtensionType.User).then(local => { - const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, local.galleryIdentifier))); + const recommendations = filteredRecs.filter(({ extensionId }) => local.every(local => !areSameExtensions({ id: extensionId }, local.identifier))); if (!recommendations.length) { return Promise.resolve(undefined); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 4de31acba3b..13b4c5b8462 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -748,7 +748,7 @@ export class InstallInRemoteServerAction extends ExtensionAction { this.storageService.store('askToInstallRemoteServerExtension', false, StorageScope.GLOBAL); } } - const galleryExtension = this.extension.gallery ? this.extension.gallery : await this.extensionGalleryService.getExtension(this.extension.local!.galleryIdentifier); + 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 { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index 6fe7ca63d57..22e10253789 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -14,7 +14,6 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import product from 'vs/platform/node/product'; @@ -48,9 +47,9 @@ export class KeymapExtensions implements IWorkbenchContribution { private checkForOtherKeymaps(extensionIdentifier: IExtensionIdentifier): Promise { return this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => { const keymaps = extensions.filter(extension => isKeymapExtension(this.tipsService, extension)); - const extension = arrays.first(keymaps, extension => stripVersion(extension.identifier.id) === extensionIdentifier.id); + const extension = arrays.first(keymaps, extension => areSameExtensions(extension.identifier, extensionIdentifier)); if (extension && extension.globallyEnabled) { - const otherKeymaps = keymaps.filter(extension => stripVersion(extension.identifier.id) !== extensionIdentifier.id && extension.globallyEnabled); + const otherKeymaps = keymaps.filter(extension => !areSameExtensions(extension.identifier, extensionIdentifier) && extension.globallyEnabled); if (otherKeymaps.length) { return this.promptForDisablingOtherKeymaps(extension, otherKeymaps); } @@ -105,7 +104,7 @@ export function onExtensionChanged(accessor: ServicesAccessor): Event(Event.any( Event.chain(Event.any(onDidInstallExtension, extensionService.onDidUninstallExtension)) - .map(e => ({ id: stripVersion(e.identifier.id), uuid: e.identifier.uuid })) + .map(e => e.identifier) .event, extensionEnablementService.onEnablementChanged ), (list, id) => { @@ -126,9 +125,9 @@ export function getInstalledExtensions(accessor: ServicesAccessor): Promise { return extensions.map(extension => { return { - identifier: extension.galleryIdentifier, + identifier: extension.identifier, local: extension, - globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.galleryIdentifier)) + globallyEnabled: disabledExtensions.every(disabled => !areSameExtensions(disabled, extension.identifier)) }; }); }); @@ -137,11 +136,7 @@ export function getInstalledExtensions(accessor: ServicesAccessor): Promise areSameExtensions({ id: extensionId }, extension.local.galleryIdentifier)); -} - -function stripVersion(id: string): string { - return getIdAndVersionFromLocalExtensionId(id).id; + return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(({ extensionId }) => areSameExtensions({ id: extensionId }, extension.local.identifier)); } export function getKeywordsForExtension(extension: string): string[] { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index 18cf1514789..d3c66db79c2 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -635,13 +635,13 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { return this.extensionsManagementService.getInstalled(ExtensionType.User).then(installed => { const maliciousExtensions = installed - .filter(e => maliciousSet.has(e.galleryIdentifier.id)); + .filter(e => maliciousSet.has(e.identifier.id)); if (maliciousExtensions.length) { return Promise.all(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e, true).then(() => { this.notificationService.prompt( Severity.Warning, - localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.galleryIdentifier.id), + localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.identifier.id), [{ label: localize('reloadNow', "Reload Now"), run: () => this.windowService.reloadWindow() diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts index ee4d08b28bb..d28726e3bb0 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViews.ts @@ -232,7 +232,7 @@ export class ExtensionsListView extends ViewletPanel { && e.local.manifest.contributes && Array.isArray(e.local.manifest.contributes.grammars) && e.local.manifest.contributes.grammars.length - && e.local.galleryIdentifier.id !== 'vscode.git'; + && e.local.identifier.id !== 'vscode.git'; }); return this.getPagedModel(this.sortExtensions(basics, options)); } @@ -240,7 +240,7 @@ export class ExtensionsListView extends ViewletPanel { const others = result.filter(e => { return e.local.manifest && e.local.manifest.contributes - && (!Array.isArray(e.local.manifest.contributes.grammars) || e.local.galleryIdentifier.id === 'vscode.git') + && (!Array.isArray(e.local.manifest.contributes.grammars) || e.local.identifier.id === 'vscode.git') && !Array.isArray(e.local.manifest.contributes.themes); }); return this.getPagedModel(this.sortExtensions(others, options)); diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 94b58d437d6..0800d2a3af9 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -16,7 +16,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, InstallExtensionEvent, DidInstallExtensionEvent, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet, getLocalExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, areSameExtensions, getMaliciousExtensionsSet } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWindowService } from 'vs/platform/windows/common/windows'; @@ -37,7 +37,7 @@ 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 } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; interface IExtensionStateProvider { (extension: Extension): T; @@ -78,11 +78,11 @@ class Extension implements IExtension { if (this.gallery) { return this.gallery.identifier; } - return this.local.galleryIdentifier; + return this.local.identifier; } get uuid(): string | undefined { - return this.gallery ? this.gallery.identifier.uuid : this.local.galleryIdentifier.uuid; + return this.gallery ? this.gallery.identifier.uuid : this.local.identifier.uuid; } get publisher(): string { @@ -433,12 +433,12 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, .then(installed => this.getDistinctInstalledExtensions(installed) .then(distinctInstalled => { const installedById = index(this.installed, e => e.identifier.id); - const groupById = groupBy(installed, i => i.galleryIdentifier.id); + const groupById = groupBy(installed, i => i.identifier.id); this.installed = distinctInstalled.map(local => { - const locals = groupById[local.galleryIdentifier.id]; + const locals = groupById[local.identifier.id]; locals.splice(locals.indexOf(local), 1); locals.splice(0, 0, local); - const extension = installedById[local.galleryIdentifier.id] || new Extension(this.galleryService, this.stateProvider, locals, undefined, this.telemetryService, this.logService, this.fileService); + 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; @@ -498,7 +498,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, 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.galleryIdentifier)); + const isDisabled = disabledExtensionIdentifiers.some(identifier => areSameExtensions(identifier, extension.identifier)); if (isDisabled) { return extension.location.scheme === Schemas.file ? 'disabled:primary' : 'disabled:secondary'; } else { @@ -511,20 +511,20 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, for (const extension of (groups['enabled'] || [])) { if (runtimeExtensions.some(r => r.extensionLocation.toString() === extension.location.toString())) { enabled.push(extension); - seenExtensions[extension.galleryIdentifier.id] = true; + seenExtensions[extension.identifier.id] = true; } else { notRunningExtensions.push(extension); } } for (const extension of notRunningExtensions) { - if (!seenExtensions[extension.galleryIdentifier.id]) { + if (!seenExtensions[extension.identifier.id]) { enabled.push(extension); - seenExtensions[extension.galleryIdentifier.id] = true; + seenExtensions[extension.identifier.id] = true; } } const primaryDisabled = groups['disabled:primary'] || []; const secondaryDisabled = (groups['disabled:secondary'] || []).filter(disabled => { - return primaryDisabled.every(p => !areSameExtensions(p.galleryIdentifier, disabled.galleryIdentifier)); + return primaryDisabled.every(p => !areSameExtensions(p.identifier, disabled.identifier)); }); return [...enabled, ...primaryDisabled, ...secondaryDisabled]; }); @@ -533,10 +533,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, private hasDuplicates(extensions: ILocalExtension[]): boolean { const seen: { [key: string]: boolean; } = Object.create(null); for (const i of extensions) { - if (seen[i.galleryIdentifier.id]) { + if (seen[i.identifier.id]) { return true; } - seen[i.galleryIdentifier.id] = true; + seen[i.identifier.id] = true; } return false; } @@ -646,7 +646,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, const toUpdate = this.local.filter(e => e.outdated && e.state !== ExtensionState.Installing - && e.local && !this.isAutoUpdateIgnored(e.local.galleryIdentifier.id, e.local.manifest.version)); + && e.local && !this.isAutoUpdateIgnored(new ExtensionIdentifierWithVersion(e.identifier, e.version))); return Promise.all(toUpdate.map(e => this.install(e))); } @@ -709,7 +709,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, title: nls.localize('uninstallingExtension', 'Uninstalling extension....'), - source: `${toUninstall[0].galleryIdentifier.id}` + source: `${toUninstall[0].identifier.id}` }, () => Promise.all(toUninstall.map(local => this.extensionService.uninstall(local))).then(() => undefined)); } @@ -731,7 +731,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, () => this.extensionService.installFromGallery(gallery) .then(() => { if (extension.latestVersion !== version) { - this.ignoreAutoUpdate(gallery.identifier.id, version); + this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version)); } }) , gallery.displayName); @@ -752,7 +752,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, - source: `${toReinstall[0].galleryIdentifier.id}` + source: `${toReinstall[0].identifier.id}` }, () => Promise.all(toReinstall.map(local => this.extensionService.reinstallFromGallery(local))).then(() => undefined)); } @@ -952,34 +952,30 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this._onChange.fire(error ? undefined : extension); } - private onUninstallExtension({ id }: IExtensionIdentifier): void { - this.logService.info(`Uninstalling the extension ${id} from window ${this.windowService.getCurrentWindowId()}`); - const extension = this.installed.filter(e => e.local.identifier.id === id)[0]; - const newLength = this.installed.filter(e => e.local.identifier.id !== id).length; - // TODO: Ask @Joao why is this? - if (newLength === this.installed.length) { + private onUninstallExtension(identifier: IExtensionIdentifier): void { + const extension = this.installed.filter(e => areSameExtensions(e.identifier, identifier))[0]; + if (!extension) { return; } - const uninstalling = this.uninstalling.filter(e => e.local.identifier.id === id)[0] || extension; - this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => e.local.identifier.id !== id)]; + const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0] || extension; + this.uninstalling = [uninstalling, ...this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier))]; - this._onChange.fire(undefined); + this._onChange.fire(uninstalling); } private onDidUninstallExtension({ identifier, error }: DidUninstallExtensionEvent): void { - const id = identifier.id; if (!error) { - this.installed = this.installed.filter(e => e.local.identifier.id !== id); + this.installed = this.installed.filter(e => !areSameExtensions(e.identifier, identifier)); } - const uninstalling = this.uninstalling.filter(e => e.local.identifier.id === id)[0]; - this.uninstalling = this.uninstalling.filter(e => e.local.identifier.id !== id); + const uninstalling = this.uninstalling.filter(e => areSameExtensions(e.identifier, identifier))[0]; + this.uninstalling = this.uninstalling.filter(e => !areSameExtensions(e.identifier, identifier)); if (!uninstalling) { return; } - this._onChange.fire(undefined); + this._onChange.fire(uninstalling); } private onEnablementChanged(identifier: IExtensionIdentifier) { @@ -1084,19 +1080,18 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this.storageService.store('extensions.ignoredAutoUpdateExtension', JSON.stringify(this._ignoredAutoUpdateExtensions), StorageScope.GLOBAL); } - private ignoreAutoUpdate(galleryId: string, version: string): void { - if (!this.isAutoUpdateIgnored(galleryId, version)) { - this.ignoredAutoUpdateExtensions = [...this.ignoredAutoUpdateExtensions, getLocalExtensionId(galleryId, version)]; + private ignoreAutoUpdate(identifierWithVersion: ExtensionIdentifierWithVersion): void { + if (!this.isAutoUpdateIgnored(identifierWithVersion)) { + this.ignoredAutoUpdateExtensions = [...this.ignoredAutoUpdateExtensions, identifierWithVersion.key()]; } } - private isAutoUpdateIgnored(galleryId: string, version: string): boolean { - const extensionId = getLocalExtensionId(galleryId, version).toLowerCase(); - return this.ignoredAutoUpdateExtensions.indexOf(extensionId) !== -1; + private isAutoUpdateIgnored(identifierWithVersion: ExtensionIdentifierWithVersion): boolean { + return this.ignoredAutoUpdateExtensions.indexOf(identifierWithVersion.key()) !== -1; } private resetIgnoreAutoUpdateExtensions(): void { - this.ignoredAutoUpdateExtensions = this.ignoredAutoUpdateExtensions.filter(extensionId => this.local.some(local => !!local.local && local.local.identifier.id.toLowerCase() === extensionId)); + this.ignoredAutoUpdateExtensions = this.ignoredAutoUpdateExtensions.filter(extensionId => this.local.some(local => !!local.local && new ExtensionIdentifierWithVersion(local.identifier, local.version).key() === extensionId)); } dispose(): void { 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 891cf0c6955..9e467e12658 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 @@ -13,7 +13,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, EnablementState, InstallOperation, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; @@ -1109,11 +1109,11 @@ suite('ExtensionsActions Test', () => { return instantiationService.get(IExtensionsWorkbenchService).queryGallery() .then((paged) => { testObject.extension = paged.firstPage[0]; - const identifier = { id: getLocalExtensionIdFromGallery(gallery, gallery.version) }; - installEvent.fire({ identifier: identifier, gallery }); - didInstallEvent.fire({ identifier: identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, { identifier }) }); + const identifier = gallery.identifier; + installEvent.fire({ identifier, gallery }); + didInstallEvent.fire({ identifier, gallery, operation: InstallOperation.Install, local: aLocalExtension('a', gallery, { identifier }) }); uninstallEvent.fire(identifier); - didUninstallEvent.fire({ identifier: identifier }); + didUninstallEvent.fire({ identifier }); assert.ok(!testObject.enabled); }); @@ -1152,9 +1152,9 @@ suite('ExtensionsActions Test', () => { didUninstallEvent.fire({ identifier: local.identifier }); const gallery = aGalleryExtension('a'); - const id = getLocalExtensionIdFromGallery(gallery, gallery.version); - installEvent.fire({ identifier: { id }, gallery }); - didInstallEvent.fire({ identifier: { id }, gallery, operation: InstallOperation.Install, local }); + const identifier = gallery.identifier; + installEvent.fire({ identifier, gallery }); + didInstallEvent.fire({ identifier, gallery, operation: InstallOperation.Install, local }); assert.ok(!testObject.enabled); }); @@ -1345,8 +1345,7 @@ suite('ExtensionsActions Test', () => { properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getLocalExtensionIdFromManifest(manifest) }, - galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } }, properties); return Object.create({ manifest, ...properties }); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index 53a3b27a696..3efccbcace4 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -14,7 +14,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, IQueryOptions, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionManagementServerService, IExtensionManagementServer, EnablementState, ExtensionRecommendationReason, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getLocalExtensionIdFromManifest } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; @@ -124,11 +124,11 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IExtensionService, { getExtensions: () => { return Promise.resolve([ - { identifier: new ExtensionIdentifier(localEnabledTheme.galleryIdentifier.id) }, - { identifier: new ExtensionIdentifier(localEnabledLanguage.galleryIdentifier.id) }, - { identifier: new ExtensionIdentifier(localRandom.galleryIdentifier.id) }, - { identifier: new ExtensionIdentifier(builtInTheme.galleryIdentifier.id) }, - { identifier: new ExtensionIdentifier(builtInBasic.galleryIdentifier.id) } + { identifier: new ExtensionIdentifier(localEnabledTheme.identifier.id) }, + { identifier: new ExtensionIdentifier(localEnabledLanguage.identifier.id) }, + { identifier: new ExtensionIdentifier(localRandom.identifier.id) }, + { identifier: new ExtensionIdentifier(builtInTheme.identifier.id) }, + { identifier: new ExtensionIdentifier(builtInBasic.identifier.id) } ]); } }); @@ -503,8 +503,7 @@ suite('ExtensionsListView Tests', () => { properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getLocalExtensionIdFromManifest(manifest) }, - galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } }, properties); return Object.create({ manifest, ...properties }); 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 466d98599e4..7b7ec835cfb 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 @@ -14,7 +14,7 @@ import { IExtensionManagementService, IExtensionGalleryService, IExtensionEnablementService, IExtensionTipsService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, EnablementState, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { getGalleryExtensionId, getLocalExtensionIdFromManifest, getLocalExtensionIdFromGallery } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionTipsService } from 'vs/workbench/parts/extensions/electron-browser/extensionTipsService'; import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test'; @@ -329,7 +329,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { assert.equal(ExtensionState.Uninstalled, extension.state); testObject.install(extension); - const identifier = { id: getLocalExtensionIdFromGallery(gallery, gallery.version) }; + const identifier = gallery.identifier; // Installing installEvent.fire({ identifier, gallery }); @@ -1186,8 +1186,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { properties = assign({ type: ExtensionType.User, location: URI.file(`pub.${name}`), - identifier: { id: getLocalExtensionIdFromManifest(manifest) }, - galleryIdentifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, + identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined }, metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' } }, properties); return Object.create({ manifest, ...properties }); diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts index ab549ec5d9e..3a6380c53e4 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferencesSearch.ts @@ -49,7 +49,7 @@ export class PreferencesSearchService extends Disposable implements IPreferences return exts .filter(ext => this.extensionEnablementService.isEnabled(ext)) .filter(ext => ext.manifest && ext.manifest.contributes && ext.manifest.contributes.configuration) - .filter(ext => !!ext.galleryIdentifier.uuid); + .filter(ext => !!ext.identifier.uuid); }); } @@ -197,7 +197,7 @@ class RemoteSearchProvider implements ISearchProvider { .filter(k => { const result = remoteResult.scoredResults[k]; const resultExtId = (result.extensionPublisher + '.' + result.extensionName).toLowerCase(); - return !installedExtensions.some(ext => ext.galleryIdentifier.id.toLowerCase() === resultExtId); + return !installedExtensions.some(ext => ext.identifier.id.toLowerCase() === resultExtId); }) .filter(k => remoteResult.scoredResults[k].score >= newExtsMinScore); @@ -393,7 +393,7 @@ class RemoteSearchProvider implements ISearchProvider { } private getExtensionFilter(ext: ILocalExtension): string { - const uuid = ext.galleryIdentifier.uuid; + const uuid = ext.identifier.uuid; const versionString = ext.manifest.version .split('.') .map(versionPart => strings.pad(versionPart, 10)) 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 ce83aa005e0..8618298189e 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -35,7 +35,6 @@ import { getExtraColor } from 'vs/workbench/parts/welcome/walkThrough/node/walkT import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/common/extensions'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IEditorInputFactory, EditorInput } from 'vs/workbench/common/editor'; -import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { TimeoutTimer } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; @@ -385,7 +384,7 @@ class WelcomePage { this.updateInstalledExtensions(container, installedExtensions); this.disposables.push(this.instantiationService.invokeFunction(onExtensionChanged)(ids => { for (const id of ids) { - if (container.querySelector(`.installExtension[data-extension="${stripVersion(id.id)}"], .enabledExtension[data-extension="${stripVersion(id.id)}"]`)) { + if (container.querySelector(`.installExtension[data-extension="${id.id}"], .enabledExtension[data-extension="${id.id}"]`)) { const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); this.updateInstalledExtensions(container, installedExtensions); break; @@ -442,7 +441,7 @@ class WelcomePage { extensionId: extensionSuggestion.id, }); this.instantiationService.invokeFunction(getInstalledExtensions).then(extensions => { - const installedExtension = arrays.first(extensions, extension => stripVersion(extension.identifier.id) === extensionSuggestion.id); + const installedExtension = arrays.first(extensions, extension => areSameExtensions(extension.identifier, { id: extensionSuggestion.id })); if (installedExtension && installedExtension.globallyEnabled) { /* __GDPR__FRAGMENT__ "WelcomePageInstalled-1" : { @@ -468,7 +467,7 @@ class WelcomePage { return this.extensionManagementService.installFromGallery(extension) .then(() => this.extensionManagementService.getInstalled(ExtensionType.User)) .then(installed => { - const local = installed.filter(i => areSameExtensions(extension.identifier, i.galleryIdentifier))[0]; + const local = installed.filter(i => areSameExtensions(extension.identifier, i.identifier))[0]; // TODO: Do this as part of the install to avoid multiple events. return this.extensionEnablementService.setEnablement(local, EnablementState.Disabled).then(() => local); }); @@ -587,7 +586,7 @@ class WelcomePage { elements[i].classList.remove('installed'); } extensions.filter(ext => ext.globallyEnabled) - .map(ext => stripVersion(ext.identifier.id)) + .map(ext => ext.identifier.id) .forEach(id => { const install = container.querySelectorAll(`.installExtension[data-extension="${id}"]`); for (let i = 0; i < install.length; i++) { @@ -606,11 +605,6 @@ class WelcomePage { } } -function stripVersion(id: string): string { - return getIdAndVersionFromLocalExtensionId(id).id; -} - - export class WelcomeInputFactory implements IEditorInputFactory { static readonly ID = welcomeInputTypeId; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 498bc475c95..2775cf89bde 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -493,7 +493,7 @@ export class ExtensionService extends Disposable implements IExtensionService { if (extensionsToDisable.length) { return this._extensionManagementService.getInstalled(ExtensionType.User) .then(installed => { - const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions(i.galleryIdentifier, e))); + const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions(i.identifier, e))); return Promise.all(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); }) .then(() => { diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index f8348fed2f6..08710361143 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -148,7 +148,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { private async handleUnhandledURL(uri: URI, extensionIdentifier: IExtensionIdentifier): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(); - const extension = installedExtensions.filter(e => areSameExtensions(e.galleryIdentifier, extensionIdentifier))[0]; + const extension = installedExtensions.filter(e => areSameExtensions(e.identifier, extensionIdentifier))[0]; // Extension is installed if (extension) { diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 4e24ae4dc2d..34af4222221 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -12,11 +12,10 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; -import { getGalleryExtensionId, getLocalExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { getIdAndVersionFromLocalExtensionId } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; +import { getGalleryExtensionId, groupByExtension } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isValidExtensionVersion } from 'vs/platform/extensions/node/extensionValidator'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, ExtensionIdentifierWithVersion } from 'vs/platform/extensions/common/extensions'; const MANIFEST_FILE = 'package.json'; @@ -557,27 +556,13 @@ export class ExtensionScanner { refs.sort((a, b) => a.name < b.name ? -1 : 1); if (!isBuiltin) { - // TODO: align with extensionsService - const nonGallery: IExtensionReference[] = []; - const gallery: IExtensionReference[] = []; - - refs.forEach(ref => { - if (ref.name.indexOf('.') !== 0) { // Do not consider user extension folder starting with `.` - const { id, version } = getIdAndVersionFromLocalExtensionId(ref.name); - if (!id || !version) { - nonGallery.push(ref); - } else { - gallery.push(ref); - } - } - }); - refs = [...nonGallery, ...gallery]; + refs = refs.filter(ref => ref.name.indexOf('.') !== 0); // Do not consider user extension folder starting with `.` } const nlsConfig = ExtensionScannerInput.createNLSConfig(input); let _extensionDescriptions = await Promise.all(refs.map(r => this.scanExtension(input.ourVersion, log, r.path, isBuiltin, isUnderDevelopment, nlsConfig))); let extensionDescriptions = arrays.coalesce(_extensionDescriptions); - extensionDescriptions = extensionDescriptions.filter(item => item !== null && !obsolete[getLocalExtensionId(getGalleryExtensionId(item.publisher, item.name), item.version)]); + extensionDescriptions = extensionDescriptions.filter(item => item !== null && !obsolete[new ExtensionIdentifierWithVersion({ id: getGalleryExtensionId(item.publisher, item.name) }, item.version).key()]); if (!isBuiltin) { // Filter out outdated extensions diff --git a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts index d4cfd888c38..80c4ac2f824 100644 --- a/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/workbenchIssueService.ts @@ -40,7 +40,7 @@ export class WorkbenchIssueService implements IWorkbenchIssueService { repositoryUrl: manifest.repository && manifest.repository.url, bugsUrl: manifest.bugs && manifest.bugs.url, displayName: manifest.displayName, - id: extension.galleryIdentifier.id, + id: extension.identifier.id, isTheme: isTheme }; }); From 75537f806c9bfb6c917f0c11003cf3f3756df40c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 14 Jan 2019 17:16:40 +0100 Subject: [PATCH 0233/1066] let IExtensionDescription extend IExtensionManifest --- .../parts/debug/test/node/debugger.test.ts | 3 +- .../services/extensions/common/extensions.ts | 29 ++++++++----------- .../electron-browser/extensionService.ts | 23 +++++---------- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/vs/workbench/parts/debug/test/node/debugger.test.ts b/src/vs/workbench/parts/debug/test/node/debugger.test.ts index 1086089ae2a..936bd120a7a 100644 --- a/src/vs/workbench/parts/debug/test/node/debugger.test.ts +++ b/src/vs/workbench/parts/debug/test/node/debugger.test.ts @@ -13,6 +13,7 @@ import { URI } from 'vs/base/common/uri'; import { ExecutableDebugAdapter } from 'vs/workbench/parts/debug/node/debugAdapter'; import { TestTextResourcePropertiesService } from 'vs/workbench/test/workbenchTestServices'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('Debug - Debugger', () => { @@ -48,7 +49,7 @@ suite('Debug - Debugger', () => { ] }; - const extensionDescriptor0 = { + const extensionDescriptor0 = { id: 'adapter', identifier: new ExtensionIdentifier('adapter'), name: 'myAdapter', diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 130c99993d7..4c5b140c8c5 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -8,29 +8,15 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -export interface IExtensionDescription { +export interface IExtensionDescription extends IExtensionManifest { readonly identifier: ExtensionIdentifier; - readonly name: string; readonly uuid?: string; - readonly displayName?: string; - readonly version: string; - readonly publisher: string; readonly isBuiltin: boolean; readonly isUnderDevelopment: boolean; readonly extensionLocation: URI; - readonly extensionDependencies?: string[]; - readonly activationEvents?: string[]; - readonly engines: { - vscode: string; - }; - readonly main?: string; - readonly contributes?: { [point: string]: any; }; - readonly keywords?: string[]; - readonly repository?: { - url: string; - }; enableProposedApi?: boolean; } @@ -247,3 +233,12 @@ export function checkProposedApiEnabled(extension: IExtensionDescription): void export function throwProposedApiError(extension: IExtensionDescription): never { throw new Error(`[${extension.identifier.value}]: Proposed API is only available when running out of dev or with the following command line switch: --enable-proposed-api ${extension.identifier.value}`); } + +export function toExtension(extensionDescription: IExtensionDescription): IExtension { + return { + type: extensionDescription.isBuiltin ? ExtensionType.System : ExtensionType.User, + identifier: { id: getGalleryExtensionId(extensionDescription.publisher, extensionDescription.name), uuid: extensionDescription.uuid }, + manifest: extensionDescription, + location: extensionDescription.extensionLocation, + }; +} diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 2775cf89bde..ece7e4dbd01 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -13,7 +13,7 @@ import * as perf from 'vs/base/common/performance'; import { isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EnablementState, IExtensionEnablementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -22,14 +22,14 @@ import product from 'vs/platform/node/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent } from 'vs/workbench/services/extensions/common/extensions'; +import { ActivationTimes, ExtensionPointContribution, IExtensionDescription, IExtensionService, IExtensionsStatus, IMessage, ProfileSession, IWillActivateEvent, IResponsiveStateChangeEvent, toExtension } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser, schema } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ExtensionHostProcessWorker } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/node/extensionDescriptionRegistry'; import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager'; -import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -86,8 +86,7 @@ export class ExtensionService extends Disposable implements IExtensionService { @ITelemetryService private readonly _telemetryService: ITelemetryService, @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, @IWindowService private readonly _windowService: IWindowService, - @ILifecycleService private readonly _lifecycleService: ILifecycleService, - @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService + @ILifecycleService private readonly _lifecycleService: ILifecycleService ) { super(); this._extensionHostLogsLocation = URI.file(path.posix.join(this._environmentService.logsPath, `exthost${this._windowService.getCurrentWindowId()}`)); @@ -439,7 +438,7 @@ export class ExtensionService extends Disposable implements IExtensionService { .then(disabledExtensions => { const runtimeExtensions: IExtensionDescription[] = []; - const extensionsToDisable: IExtensionIdentifier[] = []; + const extensionsToDisable: IExtensionDescription[] = []; const userMigratedSystemExtensions: IExtensionIdentifier[] = [{ id: BetterMergeId }]; let enableProposedApiFor: string | string[] = this._environmentService.args['enable-proposed-api'] || []; @@ -478,7 +477,7 @@ export class ExtensionService extends Disposable implements IExtensionService { // Check if the extension is changed to system extension const userMigratedSystemExtension = userMigratedSystemExtensions.filter(userMigratedSystemExtension => areSameExtensions(userMigratedSystemExtension, { id: extension.identifier.value }))[0]; if (userMigratedSystemExtension) { - extensionsToDisable.push(userMigratedSystemExtension); + extensionsToDisable.push(extension); continue; } } @@ -491,14 +490,8 @@ export class ExtensionService extends Disposable implements IExtensionService { }); if (extensionsToDisable.length) { - return this._extensionManagementService.getInstalled(ExtensionType.User) - .then(installed => { - const toDisable = installed.filter(i => extensionsToDisable.some(e => areSameExtensions(i.identifier, e))); - return Promise.all(toDisable.map(e => this._extensionEnablementService.setEnablement(e, EnablementState.Disabled))); - }) - .then(() => { - return runtimeExtensions; - }); + return Promise.all(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(toExtension(e), EnablementState.Disabled))) + .then(() => runtimeExtensions); } else { return runtimeExtensions; } From 2fe9de851100cf6a1d5c3ff41849d8b5b2ebb4a4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 14 Jan 2019 17:04:29 +0100 Subject: [PATCH 0234/1066] Initialize configuration data through ExtHostConfiguration --- .../mainThreadConfiguration.ts | 20 +++++++-- src/vs/workbench/api/node/extHost.api.impl.ts | 20 ++++----- src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../api/node/extHostConfiguration.ts | 44 +++++++++++++++--- .../workbench/api/node/extHostDebugService.ts | 11 ++--- .../api/node/extHostExtensionService.ts | 19 +++++--- src/vs/workbench/api/node/extHostSearch.ts | 8 ++-- src/vs/workbench/api/node/extHostTask.ts | 7 +-- .../api/node/extHostTerminalService.ts | 6 +-- src/vs/workbench/node/extensionHostMain.ts | 2 +- src/vs/workbench/node/proxyResolver.ts | 24 +++++----- .../electron-browser/extensionHost.ts | 8 +--- .../electron-browser/extensionService.ts | 10 +++-- .../api/extHostConfiguration.test.ts | 12 ++--- .../api/extHostSearch.test.ts | 8 +++- .../api/mainThreadConfiguration.test.ts | 45 +++++++++++-------- 16 files changed, 155 insertions(+), 93 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts index 6c37740e3d4..0d91a9b058a 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadConfiguration.ts @@ -6,12 +6,13 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; -import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData } from '../node/extHost.protocol'; +import { MainThreadConfigurationShape, MainContext, ExtHostContext, IExtHostContext, IWorkspaceConfigurationChangeEventData, IConfigurationInitData } from '../node/extHost.protocol'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationModel } from 'vs/platform/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @extHostNamedCustomer(MainContext.MainThreadConfiguration) export class MainThreadConfiguration implements MainThreadConfigurationShape { @@ -21,15 +22,26 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { constructor( extHostContext: IExtHostContext, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, - @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService + @IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, ) { const proxy = extHostContext.getProxy(ExtHostContext.ExtHostConfiguration); + proxy.$initializeConfiguration(this._getConfigurationData()); this._configurationListener = configurationService.onDidChangeConfiguration(e => { - proxy.$acceptConfigurationChanged(configurationService.getConfigurationData(), this.toConfigurationChangeEventData(e)); + proxy.$acceptConfigurationChanged(this._getConfigurationData(), this.toConfigurationChangeEventData(e)); }); } + private _getConfigurationData(): IConfigurationInitData { + const configurationData: IConfigurationInitData = { ...this.configurationService.getConfigurationData(), configurationScopes: {} }; + // Send configurations scopes only in development mode. + if (!this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment) { + configurationData.configurationScopes = getScopes(); + } + return configurationData; + } + public dispose(): void { this._configurationListener.dispose(); } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ddf6e61247e..f044aa65682 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -24,7 +24,7 @@ import { ExtHostApiCommands } from 'vs/workbench/api/node/extHostApiCommands'; import { ExtHostClipboard } from 'vs/workbench/api/node/extHostClipboard'; import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; import { ExtHostComments } from 'vs/workbench/api/node/extHostComments'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService'; import { ExtHostDecorations } from 'vs/workbench/api/node/extHostDecorations'; import { ExtHostDiagnostics } from 'vs/workbench/api/node/extHostDiagnostics'; @@ -66,7 +66,7 @@ import * as vscode from 'vscode'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; export interface IExtensionApiFactory { - (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry): typeof vscode; + (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; } function proposedApiFunction(extension: IExtensionDescription, fn: T): T { @@ -142,7 +142,7 @@ export function createApiFactory( // Register API-ish commands ExtHostApiCommands.register(extHostCommands); - return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry): typeof vscode { + return function (extension: IExtensionDescription, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode { // Check document selectors for being overly generic. Technically this isn't a problem but // in practice many extensions say they support `fooLang` but need fs-access to do so. Those @@ -598,11 +598,11 @@ export function createApiFactory( return extHostDocumentSaveParticipant.getOnWillSaveTextDocumentEvent(extension)(listener, thisArgs, disposables); }, onDidChangeConfiguration: (listener: (_: any) => any, thisArgs?: any, disposables?: extHostTypes.Disposable[]) => { - return extHostConfiguration.onDidChangeConfiguration(listener, thisArgs, disposables); + return configProvider.onDidChangeConfiguration(listener, thisArgs, disposables); }, getConfiguration(section?: string, resource?: vscode.Uri): vscode.WorkspaceConfiguration { resource = arguments.length === 1 ? undefined : resource; - return extHostConfiguration.getConfiguration(section, resource, extension.identifier); + return configProvider.getConfiguration(section, resource, extension.identifier); }, registerTextDocumentContentProvider(scheme: string, provider: vscode.TextDocumentContentProvider) { return extHostDocumentContentProviders.registerTextDocumentContentProvider(scheme, provider); @@ -865,11 +865,11 @@ class Extension implements vscode.Extension { } } -export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry): Promise { - return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry)); +export function initializeExtensionApi(extensionService: ExtHostExtensionService, apiFactory: IExtensionApiFactory, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): Promise { + return extensionService.getExtensionPathIndex().then(trie => defineAPI(apiFactory, trie, extensionRegistry, configProvider)); } -function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree, extensionRegistry: ExtensionDescriptionRegistry): void { +function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchTree, extensionRegistry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): void { // each extension is meant to get its own api implementation const extApiImpl = new Map(); @@ -887,7 +887,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT if (ext) { let apiImpl = extApiImpl.get(ExtensionIdentifier.toKey(ext.identifier)); if (!apiImpl) { - apiImpl = factory(ext, extensionRegistry); + apiImpl = factory(ext, extensionRegistry, configProvider); extApiImpl.set(ExtensionIdentifier.toKey(ext.identifier), apiImpl); } return apiImpl; @@ -898,7 +898,7 @@ function defineAPI(factory: IExtensionApiFactory, extensionPaths: TernarySearchT let extensionPathsPretty = ''; extensionPaths.forEach((value, index) => extensionPathsPretty += `\t${index} -> ${value.identifier.value}\n`); console.warn(`Could not identify extension for 'vscode' require call from ${parent.filename}. These are the extension path mappings: \n${extensionPathsPretty}`); - defaultApiImpl = factory(nullExtensionDescription, extensionRegistry); + defaultApiImpl = factory(nullExtensionDescription, extensionRegistry, configProvider); } return defaultApiImpl; }; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b1120075a6c..d6341733d16 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -67,7 +67,6 @@ export interface IInitData { environment: IEnvironment; workspace: IWorkspaceData; extensions: IExtensionDescription[]; - configuration: IConfigurationInitData; telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; @@ -632,7 +631,8 @@ export interface ExtHostCommandsShape { } export interface ExtHostConfigurationShape { - $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData): void; + $initializeConfiguration(data: IConfigurationInitData): void; + $acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void; } export interface ExtHostDiagnosticsShape { diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index e6abc5e414d..3e73209ffbb 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -17,6 +17,7 @@ import { ResourceMap } from 'vs/base/common/map'; import { ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry'; import { isObject } from 'vs/base/common/types'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { Barrier } from 'vs/base/common/async'; function lookUp(tree: any, key: string) { if (key) { @@ -39,6 +40,34 @@ type ConfigurationInspect = { export class ExtHostConfiguration implements ExtHostConfigurationShape { + private readonly _proxy: MainThreadConfigurationShape; + private readonly _extHostWorkspace: ExtHostWorkspace; + private readonly _barrier: Barrier; + private _actual: ExtHostConfigProvider; + + constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace) { + this._proxy = proxy; + this._extHostWorkspace = extHostWorkspace; + this._barrier = new Barrier(); + this._actual = null; + } + + public getConfigProvider(): Promise { + return this._barrier.wait().then(_ => this._actual); + } + + $initializeConfiguration(data: IConfigurationInitData): void { + this._actual = new ExtHostConfigProvider(this._proxy, this._extHostWorkspace, data); + this._barrier.open(); + } + + $acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void { + this._actual.$acceptConfigurationChanged(data, eventData); + } +} + +export class ExtHostConfigProvider { + private readonly _onDidChangeConfiguration = new Emitter(); private readonly _proxy: MainThreadConfigurationShape; private readonly _extHostWorkspace: ExtHostWorkspace; @@ -48,7 +77,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace, data: IConfigurationInitData) { this._proxy = proxy; this._extHostWorkspace = extHostWorkspace; - this._configuration = ExtHostConfiguration.parse(data); + this._configuration = ExtHostConfigProvider.parse(data); this._configurationScopes = data.configurationScopes; } @@ -56,8 +85,9 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { return this._onDidChangeConfiguration && this._onDidChangeConfiguration.event; } - $acceptConfigurationChanged(data: IConfigurationData, eventData: IWorkspaceConfigurationChangeEventData) { - this._configuration = ExtHostConfiguration.parse(data); + $acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData) { + this._configuration = ExtHostConfigProvider.parse(data); + this._configurationScopes = data.configurationScopes; this._onDidChangeConfiguration.fire(this._toConfigurationChangeEvent(eventData)); } @@ -220,11 +250,11 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { } private static parse(data: IConfigurationData): Configuration { - const defaultConfiguration = ExtHostConfiguration.parseConfigurationModel(data.defaults); - const userConfiguration = ExtHostConfiguration.parseConfigurationModel(data.user); - const workspaceConfiguration = ExtHostConfiguration.parseConfigurationModel(data.workspace); + const defaultConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.defaults); + const userConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.user); + const workspaceConfiguration = ExtHostConfigProvider.parseConfigurationModel(data.workspace); const folders: ResourceMap = Object.keys(data.folders).reduce((result, key) => { - result.set(URI.parse(key), ExtHostConfiguration.parseConfigurationModel(data.folders[key])); + result.set(URI.parse(key), ExtHostConfigProvider.parseConfigurationModel(data.folders[key])); return result; }, new ResourceMap()); return new Configuration(defaultConfiguration, userConfiguration, workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false); diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 2852ca7e4fb..fcc931e8082 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -23,7 +23,7 @@ import { ITerminalSettings, IDebuggerContribution, IConfig, IDebugAdapter, IDebu import { getTerminalLauncher, hasChildProcesses, prepareCommand } from 'vs/workbench/parts/debug/node/terminals'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { AbstractVariableResolverService } from 'vs/workbench/services/configurationResolver/node/variableResolver'; -import { ExtHostConfiguration } from './extHostConfiguration'; +import { ExtHostConfiguration, ExtHostConfigProvider } from './extHostConfiguration'; import { convertToVSCPaths, convertToDAPaths, stringToUri, uriToString } from 'vs/workbench/parts/debug/common/debugUtils'; import { ExtHostTerminalService } from 'vs/workbench/api/node/extHostTerminalService'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -359,9 +359,10 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { return undefined; } - public $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { + public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { + const configProvider = await this._configurationService.getConfigProvider(); if (!this._variableResolver) { - this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, this._configurationService); + this._variableResolver = new ExtHostVariableResolverService(this._workspaceService, this._editorsService, configProvider); } let ws: IWorkspaceFolder; const folder = this.getFolder(folderUri); @@ -375,7 +376,7 @@ export class ExtHostDebugService implements ExtHostDebugServiceShape { } }; } - return Promise.resolve(this._variableResolver.resolveAny(ws, config)); + return this._variableResolver.resolveAny(ws, config); } public $startDASession(debugAdapterHandle: number, sessionDto: IDebugSessionDto): Promise { @@ -852,7 +853,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole { export class ExtHostVariableResolverService extends AbstractVariableResolverService { - constructor(workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfiguration) { + constructor(workspaceService: ExtHostWorkspace, editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider) { super({ getFolderUri: (folderName: string): URI => { const folders = workspaceService.getWorkspaceFolders(); diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 2708455fc7a..c4b551ccd29 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -219,18 +219,25 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._started = false; - initializeExtensionApi(this, this._extensionApiFactory, this._registry).then(() => { - // Do this when extension service exists, but extensions are not being activated yet. - return connectProxyResolver(this._extHostWorkspace, this._extHostConfiguration, this, this._extHostLogService, this._mainThreadTelemetryProxy); - }).then(() => { - this._barrier.open(); - }); + this._initialize(); if (this._initData.autoStart) { this._startExtensionHost(); } } + private async _initialize(): Promise { + try { + const configProvider = await this._extHostConfiguration.getConfigProvider(); + await initializeExtensionApi(this, this._extensionApiFactory, this._registry, configProvider); + // Do this when extension service exists, but extensions are not being activated yet. + await connectProxyResolver(this._extHostWorkspace, configProvider, this, this._extHostLogService, this._mainThreadTelemetryProxy); + this._barrier.open(); + } catch (err) { + errors.onUnexpectedError(err); + } + } + public async deactivateAll(): Promise { let allPromises: Promise[] = []; try { diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 75436d5aa32..47ab14cf1a3 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -9,7 +9,7 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; import { ILogService } from 'vs/platform/log/common/log'; import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex'; import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager'; import { SearchService } from 'vs/workbench/services/search/node/rawSearchService'; @@ -46,7 +46,9 @@ export class ExtHostSearch implements ExtHostSearchShape { this._fileSearchManager = new FileSearchManager(); this._fileIndexSearchManager = new FileIndexSearchManager(); - registerEHProviders(this, _logService, configService); + configService.getConfigProvider().then((configProvider) => { + registerEHProviders(this, _logService, configProvider); + }); } private _transformScheme(scheme: string): string { @@ -176,7 +178,7 @@ export class ExtHostSearch implements ExtHostSearchShape { } } -function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogService, configService: ExtHostConfiguration) { +function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogService, configService: ExtHostConfigProvider) { if (configService.getConfiguration('searchRipgrep').enable || configService.getConfiguration('search').runInExtensionHost) { const outputChannel = new OutputChannel(logService); extHostSearch.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel)); diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 0f44a5a2a03..6bbb0f26c42 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -471,14 +471,15 @@ export class ExtHostTask implements ExtHostTaskShape { }); } - public $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { + public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { + const configProvider = await this._configurationService.getConfigProvider(); let uri: URI = URI.revive(uriComponents); let result = { process: undefined as string, variables: Object.create(null) }; let workspaceFolder = this._workspaceService.resolveWorkspaceFolder(uri); - let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, this._configurationService); + let resolver = new ExtHostVariableResolverService(this._workspaceService, this._editorService, configProvider); let ws: IWorkspaceFolder = { uri: workspaceFolder.uri, name: workspaceFolder.name, @@ -504,7 +505,7 @@ export class ExtHostTask implements ExtHostTaskShape { paths ); } - return Promise.resolve(result); + return result; } private nextHandle(): number { diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 96059529056..95ec3d5874a 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -374,11 +374,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { } } - public $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): void { + public async $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise { // TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally // they would be merged into a single implementation. - - const terminalConfig = this._extHostConfiguration.getConfiguration('terminal.integrated'); + const configProvider = await this._extHostConfiguration.getConfigProvider(); + const terminalConfig = configProvider.getConfiguration('terminal.integrated'); if (!shellLaunchConfig.executable) { // TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index af73f59ce6b..06730f4e594 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -77,7 +77,7 @@ export class ExtensionHostMain { this._extHostLogService.info('extension host started'); this._extHostLogService.trace('initData', initData); - this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace, initData.configuration); + this._extHostConfiguration = new ExtHostConfiguration(rpcProtocol.getProxy(MainContext.MainThreadConfiguration), extHostWorkspace); this._extensionService = new ExtHostExtensionService(nativeExit, initData, rpcProtocol, extHostWorkspace, this._extHostConfiguration, this._extHostLogService); // error forwarding and stack trace scanning diff --git a/src/vs/workbench/node/proxyResolver.ts b/src/vs/workbench/node/proxyResolver.ts index 737923ec8b8..c330eb973a2 100644 --- a/src/vs/workbench/node/proxyResolver.ts +++ b/src/vs/workbench/node/proxyResolver.ts @@ -9,7 +9,7 @@ import * as nodeurl from 'url'; import { assign } from 'vs/base/common/objects'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { ProxyAgent } from 'vscode-proxy-agent'; import { MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; @@ -19,13 +19,13 @@ import { URI } from 'vs/base/common/uri'; export function connectProxyResolver( extHostWorkspace: ExtHostWorkspace, - extHostConfiguration: ExtHostConfiguration, + configProvider: ExtHostConfigProvider, extensionService: ExtHostExtensionService, extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape ) { - const agent = createProxyAgent(extHostWorkspace, extHostConfiguration, extHostLogService, mainThreadTelemetry); - const lookup = createPatchedModules(extHostConfiguration, agent); + const agent = createProxyAgent(extHostWorkspace, configProvider, extHostLogService, mainThreadTelemetry); + const lookup = createPatchedModules(configProvider, agent); return configureModuleLoading(extensionService, lookup); } @@ -33,14 +33,14 @@ const maxCacheEntries = 5000; // Cache can grow twice that much due to 'oldCache function createProxyAgent( extHostWorkspace: ExtHostWorkspace, - extHostConfiguration: ExtHostConfiguration, + configProvider: ExtHostConfigProvider, extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape ) { - let settingsProxy = proxyFromConfigURL(extHostConfiguration.getConfiguration('http') + let settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http') .get('proxy')); - extHostConfiguration.onDidChangeConfiguration(e => { - settingsProxy = proxyFromConfigURL(extHostConfiguration.getConfiguration('http') + configProvider.onDidChangeConfiguration(e => { + settingsProxy = proxyFromConfigURL(configProvider.getConfiguration('http') .get('proxy')); }); const env = process.env; @@ -177,13 +177,13 @@ function proxyFromConfigURL(configURL: string) { return undefined; } -function createPatchedModules(extHostConfiguration: ExtHostConfiguration, agent: http.Agent) { +function createPatchedModules(configProvider: ExtHostConfigProvider, agent: http.Agent) { const setting = { - config: extHostConfiguration.getConfiguration('http') + config: configProvider.getConfiguration('http') .get('proxySupport') || 'off' }; - extHostConfiguration.onDidChangeConfiguration(e => { - setting.config = extHostConfiguration.getConfiguration('http') + configProvider.onDidChangeConfiguration(e => { + setting.config = configProvider.getConfiguration('http') .get('proxySupport') || 'off'; }); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 9df031a8252..a0548a72d60 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -22,7 +22,6 @@ import { findFreePort, randomPort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol, generateRandomPipeName, BufferedProtocol } from 'vs/base/parts/ipc/node/ipc.net'; import { IBroadcast, IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; -import { getScopes } from 'vs/platform/configuration/common/configurationRegistry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -33,9 +32,8 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IConfigurationInitData, IInitData } from 'vs/workbench/api/node/extHost.protocol'; +import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; @@ -102,7 +100,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { @IBroadcastService private readonly _broadcastService: IBroadcastService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWorkspaceConfigurationService private readonly _configurationService: IWorkspaceConfigurationService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @ICrashReporterService private readonly _crashReporterService: ICrashReporterService, @ILogService private readonly _logService: ILogService, @@ -415,7 +412,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private _createExtHostInitData(): Promise { return Promise.all([this._telemetryService.getTelemetryInfo(), this._extensions]) .then(([telemetryInfo, extensionDescriptions]) => { - const configurationData: IConfigurationInitData = { ...this._configurationService.getConfigurationData(), configurationScopes: {} }; const workspace = this._contextService.getWorkspace(); const r: IInitData = { commit: product.commit, @@ -435,8 +431,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { name: this._labelService.getWorkspaceLabel(workspace) }, extensions: extensionDescriptions, - // Send configurations scopes only in development mode. - configuration: !this._environmentService.isBuilt || this._environmentService.isExtensionDevelopment ? { ...configurationData, configurationScopes: getScopes() } : configurationData, telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 2775cf89bde..ea32d625ab9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -412,11 +412,13 @@ export class ExtensionService extends Disposable implements IExtensionService { const extensionHost = this._extensionHostProcessManagers[0]; const extensions = await this._extensionScanner.scannedExtensions; const enabledExtensions = await this._getRuntimeExtensions(extensions); + + this._handleExtensionPoints(enabledExtensions); extensionHost.start(enabledExtensions.map(extension => extension.identifier)); - this._onHasExtensions(enabledExtensions); + this._releaseBarrier(); } - private _onHasExtensions(allExtensions: IExtensionDescription[]): void { + private _handleExtensionPoints(allExtensions: IExtensionDescription[]): void { this._registry = new ExtensionDescriptionRegistry(allExtensions); let availableExtensions = this._registry.getAllExtensionDescriptions(); @@ -427,11 +429,13 @@ export class ExtensionService extends Disposable implements IExtensionService { for (let i = 0, len = extensionPoints.length; i < len; i++) { ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); } + } + private _releaseBarrier(): void { perf.mark('extensionHostReady'); this._installedExtensionsReady.open(); this._onDidRegisterExtensions.fire(undefined); - this._onDidChangeExtensionsStatus.fire(availableExtensions.map(e => e.identifier)); + this._onDidChangeExtensionsStatus.fire(this._registry.getAllExtensionDescriptions().map(e => e.identifier)); } private _getRuntimeExtensions(allExtensions: IExtensionDescription[]): Promise { diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 0b0da264737..995f762f4b4 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; +import { ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { MainThreadConfigurationShape, IConfigurationInitData } from 'vs/workbench/api/node/extHost.protocol'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { TestRPCProtocol } from './testRPCProtocol'; @@ -31,7 +31,7 @@ suite('ExtHostConfiguration', function () { if (!shape) { shape = new class extends mock() { }; } - return new ExtHostConfiguration(shape, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()), createConfigurationData(contents)); + return new ExtHostConfigProvider(shape, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()), createConfigurationData(contents)); } function createConfigurationData(contents: any): IConfigurationInitData { @@ -263,7 +263,7 @@ suite('ExtHostConfiguration', function () { }); test('inspect in no workspace context', function () { - const testObject = new ExtHostConfiguration( + const testObject = new ExtHostConfigProvider( new class extends mock() { }, new ExtHostWorkspace(new TestRPCProtocol(), null, new NullLogService(), new Counter()), { @@ -306,7 +306,7 @@ suite('ExtHostConfiguration', function () { } }, ['editor.wordWrap']); folders[workspaceUri.toString()] = workspace; - const testObject = new ExtHostConfiguration( + const testObject = new ExtHostConfigProvider( new class extends mock() { }, new ExtHostWorkspace(new TestRPCProtocol(), { 'id': 'foo', @@ -380,7 +380,7 @@ suite('ExtHostConfiguration', function () { }, ['editor.wordWrap']); folders[thirdRoot.toString()] = new ConfigurationModel({}, []); - const testObject = new ExtHostConfiguration( + const testObject = new ExtHostConfigProvider( new class extends mock() { }, new ExtHostWorkspace(new TestRPCProtocol(), { 'id': 'foo', @@ -589,7 +589,7 @@ suite('ExtHostConfiguration', function () { test('configuration change event', (done) => { const workspaceFolder = aWorkspaceFolder(URI.file('folder1'), 0); - const testObject = new ExtHostConfiguration( + const testObject = new ExtHostConfigProvider( new class extends mock() { }, new ExtHostWorkspace(new TestRPCProtocol(), { 'id': 'foo', diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index 0a0084ddcc7..b5ca6df3e9f 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -61,8 +61,12 @@ class MockMainThreadSearch implements MainThreadSearchShape { } class MockExtHostConfiguration { - getConfiguration(section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration { - return {}; + getConfigProvider(): Promise { + return Promise.resolve({ + getConfiguration: (section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration => { + return {}; + } + }); } } diff --git a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts index b13be5a1ccb..4be29bb9ecd 100644 --- a/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/mainThreadConfiguration.test.ts @@ -14,9 +14,13 @@ import { MainThreadConfiguration } from 'vs/workbench/api/electron-browser/mainT import { SingleProxyRPCProtocol } from './testRPCProtocol'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; suite('MainThreadConfiguration', function () { + let proxy = { + $initializeConfiguration: () => { } + }; let instantiationService: TestInstantiationService; let target: sinon.SinonSpy; @@ -50,11 +54,14 @@ suite('MainThreadConfiguration', function () { instantiationService.stub(IConfigurationService, 'onDidUpdateConfiguration', sinon.mock()); instantiationService.stub(IConfigurationService, 'onDidChangeConfiguration', sinon.mock()); instantiationService.stub(IConfigurationService, 'updateValue', target); + instantiationService.stub(IEnvironmentService, { + isBuilt: false + }); }); test('update resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); @@ -63,7 +70,7 @@ suite('MainThreadConfiguration', function () { test('update resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); @@ -72,7 +79,7 @@ suite('MainThreadConfiguration', function () { test('update resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', null); @@ -81,7 +88,7 @@ suite('MainThreadConfiguration', function () { test('update window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); @@ -90,7 +97,7 @@ suite('MainThreadConfiguration', function () { test('update window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -99,7 +106,7 @@ suite('MainThreadConfiguration', function () { test('update window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -108,7 +115,7 @@ suite('MainThreadConfiguration', function () { test('update window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.window', 'value', null); @@ -117,7 +124,7 @@ suite('MainThreadConfiguration', function () { test('update resource configuration without configuration target defaults to folder', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(null, 'extHostConfiguration.resource', 'value', URI.file('abc')); @@ -126,7 +133,7 @@ suite('MainThreadConfiguration', function () { test('update configuration with user configuration target', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(ConfigurationTarget.USER, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -135,7 +142,7 @@ suite('MainThreadConfiguration', function () { test('update configuration with workspace configuration target', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -144,7 +151,7 @@ suite('MainThreadConfiguration', function () { test('update configuration with folder configuration target', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$updateConfigurationOption(ConfigurationTarget.WORKSPACE_FOLDER, 'extHostConfiguration.window', 'value', URI.file('abc')); @@ -153,7 +160,7 @@ suite('MainThreadConfiguration', function () { test('remove resource configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); @@ -162,7 +169,7 @@ suite('MainThreadConfiguration', function () { test('remove resource configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); @@ -171,7 +178,7 @@ suite('MainThreadConfiguration', function () { test('remove resource configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', null); @@ -180,7 +187,7 @@ suite('MainThreadConfiguration', function () { test('remove window configuration without configuration target defaults to workspace in multi root workspace when no resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); @@ -189,7 +196,7 @@ suite('MainThreadConfiguration', function () { test('remove window configuration without configuration target defaults to workspace in multi root workspace when resource is provided', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); @@ -198,7 +205,7 @@ suite('MainThreadConfiguration', function () { test('remove window configuration without configuration target defaults to workspace in folder workspace when resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', URI.file('abc')); @@ -207,7 +214,7 @@ suite('MainThreadConfiguration', function () { test('remove window configuration without configuration target defaults to workspace in folder workspace when no resource is provider', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.FOLDER }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.window', null); @@ -216,7 +223,7 @@ suite('MainThreadConfiguration', function () { test('remove configuration without configuration target defaults to folder', function () { instantiationService.stub(IWorkspaceContextService, { getWorkbenchState: () => WorkbenchState.WORKSPACE }); - const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(null)); + const testObject: MainThreadConfiguration = instantiationService.createInstance(MainThreadConfiguration, SingleProxyRPCProtocol(proxy)); testObject.$removeConfigurationOption(null, 'extHostConfiguration.resource', URI.file('abc')); From 559069481c43ce664d8d2f31971bfdfdd3c772da Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 14 Jan 2019 10:32:45 -0800 Subject: [PATCH 0235/1066] Only apply re-focusing workaround when the webview is already focused Fixes #66491 --- .../parts/webview/electron-browser/webviewElement.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index a27871e6e79..b6a9ea29e72 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -231,6 +231,7 @@ export class WebviewElement extends Disposable { private _findStarted: boolean = false; private _contents: string = ''; private _state: string | undefined = undefined; + private _focused = false; private readonly _onDidFocus = this._register(new Emitter()); public get onDidFocus(): Event { return this._onDidFocus.event; } @@ -290,8 +291,10 @@ export class WebviewElement extends Disposable { this.layout(); // Workaround for https://github.com/electron/electron/issues/14474 - this._webview.blur(); - this._webview.focus(); + if (this._focused || document.activeElement === this._webview) { + this._webview.blur(); + this._webview.focus(); + } })); this._register(addDisposableListener(this._webview, 'crashed', () => { console.error('embedded page crashed'); @@ -441,6 +444,7 @@ export class WebviewElement extends Disposable { } private handleFocusChange(isFocused: boolean): void { + this._focused = isFocused; if (isFocused) { this._onDidFocus.fire(); } From 957f536a6607e266d1892d15e118d63c14b870b5 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 14 Jan 2019 22:50:09 +0100 Subject: [PATCH 0236/1066] launch EH window via VS Code --- .../parts/debug/common/debugUtils.ts | 5 + .../debug/electron-browser/debugSession.ts | 6 +- .../debug/electron-browser/rawDebugSession.ts | 114 +++++++++++++++++- .../test/electron-browser/debugModel.test.ts | 12 +- 4 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index a12f19a08eb..89ed9de8ed8 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -163,6 +163,11 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: case 'gotoTargets': fixSourcePaths(true, (request.arguments).source); break; + case 'launchVSCode': + const source = { path: request.arguments.extensionDevelopmentPath }; + fixSourcePaths(false, source); + request.arguments.extensionDevelopmentPath = source.path; + break; default: break; } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts index c5b7edff2d6..6da6f6a2f98 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts @@ -31,6 +31,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ReplModel } from 'vs/workbench/parts/debug/common/replModel'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; export class DebugSession implements IDebugSession { private id: string; @@ -60,7 +61,8 @@ export class DebugSession implements IDebugSession { @IWindowService private readonly windowService: IWindowService, @IConfigurationService private readonly configurationService: IConfigurationService, @IViewletService private readonly viewletService: IViewletService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IEnvironmentService private readonly environmentService: IEnvironmentService ) { this.id = generateUuid(); this.repl = new ReplModel(this); @@ -146,7 +148,7 @@ export class DebugSession implements IDebugSession { return dbgr.createDebugAdapter(this, this.outputService).then(debugAdapter => { - this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService); + this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.environmentService); return this.raw.start().then(() => { diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index a7eb5c52071..54b1b4c8113 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -12,6 +12,19 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { formatPII } from 'vs/workbench/parts/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; +import * as cp from 'child_process'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +export interface ILaunchVSCodeArguments { + sessionId: string; + debugMode: 'noDebug' | 'break'; + debugPort: number; + extensionDevelopmentPath: string; + options: string[]; + args: string[]; + cwd?: string; + env: { [key: string]: string | null; }; +} /** * Encapsulates the DebugAdapter lifecycle and some idiosyncrasies of the Debug Adapter Protocol. @@ -52,7 +65,8 @@ export class RawDebugSession { private debugAdapter: IDebugAdapter, dbgr: IDebugger, private telemetryService: ITelemetryService, - public customTelemetryService: ITelemetryService + public customTelemetryService: ITelemetryService, + private environmentService: IEnvironmentService ) { this._capabilities = Object.create(null); this._readyForBreakpoints = false; @@ -477,6 +491,15 @@ export class RawDebugSession { const safeSendResponse = (response) => this.debugAdapter && this.debugAdapter.sendResponse(response); switch (request.command) { + case 'launchVSCode': + this.launch(request.arguments).then(_ => { + safeSendResponse(response); + }, err => { + response.success = false; + response.message = err.message; + safeSendResponse(response); + }); + break; case 'runInTerminal': dbgr.runInTerminal(request.arguments as DebugProtocol.RunInTerminalRequestArguments).then(shellProcessId => { const resp = response as DebugProtocol.RunInTerminalResponse; @@ -514,6 +537,95 @@ export class RawDebugSession { } } + private launch(VSCodeArgs: ILaunchVSCodeArguments): Promise { + + let envArgs = process.env; + if (VSCodeArgs.env) { + // merge environment variables into a copy of the process.env + envArgs = objects.mixin(objects.mixin({}, process.env), VSCodeArgs.env); + // and delete some if necessary + Object.keys(envArgs).filter(k => envArgs[k] === null).forEach(key => delete envArgs[key]); + } + + const spawnOpts: cp.SpawnOptions = { + env: envArgs, + detached: false // https://github.com/Microsoft/vscode/issues/57018 + }; + if (VSCodeArgs.cwd) { + spawnOpts.cwd = VSCodeArgs.cwd; + } + + let spawnArgs = [].concat(VSCodeArgs.options); + + if (VSCodeArgs.debugMode !== 'noDebug') { + spawnArgs.push(`--debugBrkPluginHost=${VSCodeArgs.debugPort}`); + } + + // pass the debug session ID to the EH so that broadcast events know where they come from + if (VSCodeArgs.sessionId) { + spawnArgs.push(`--debugId=${VSCodeArgs.sessionId}`); + } + + if (VSCodeArgs.extensionDevelopmentPath) { + spawnArgs.push(`--extensionDevelopmentPath=${VSCodeArgs.extensionDevelopmentPath}`); + } + + let runtimeExecutable = this.environmentService['execPath']; + if (!runtimeExecutable) { + return Promise.reject(new Error(`VS Code executable unknown`)); + } + + // if VS Code runs out of sources, add the path to the VS Code workspace as a first argument so that Electron turns into VS Code + const electronIdx = runtimeExecutable.indexOf(process.platform === 'win32' ? '\\.build\\electron\\' : '/.build/electron/'); + if (electronIdx > 0 && VSCodeArgs.args.length > 0) { + // guess the VS Code workspace path + const vscodeWorkspacePath = runtimeExecutable.substr(0, electronIdx); + + // only add path if user hasn't already added path + if (VSCodeArgs.args.length === 0 || VSCodeArgs.args[0].indexOf(vscodeWorkspacePath) !== 0) { + spawnArgs.push(vscodeWorkspacePath); + } + } + + spawnArgs = spawnArgs.concat(VSCodeArgs.args); + + // Workaround for bug Microsoft/vscode#45832 + if (process.platform === 'win32' && runtimeExecutable.indexOf(' ') > 0) { + let foundArgWithSpace = false; + + // check whether there is one arg with a space + const args: string[] = []; + for (const a of spawnArgs) { + if (a.indexOf(' ') > 0) { + args.push(`"${a}"`); + foundArgWithSpace = true; + } else { + args.push(a); + } + } + + if (foundArgWithSpace) { + spawnArgs = args; + runtimeExecutable = `"${runtimeExecutable}"`; + spawnOpts.shell = true; + } + } + + return new Promise((resolve, reject) => { + const process = cp.spawn(runtimeExecutable, spawnArgs, spawnOpts); + process.on('error', error => { + reject(error); + }); + process.on('exit', code => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`VS Code exited with ${code}`)); + } + }); + }); + } + private send(command: string, args: any, timeout?: number): Promise { return new Promise((completeDispatch, errorDispatch) => { this.debugAdapter.sendRequest(command, args, (response: R) => { diff --git a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts index 90b9c7a103f..2fe1f43a733 100644 --- a/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts +++ b/src/vs/workbench/parts/debug/test/electron-browser/debugModel.test.ts @@ -110,7 +110,7 @@ suite('Debug - Model', () => { test('threads simple', () => { const threadId = 1; const threadName = 'firstThread'; - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); assert.equal(model.getSessions(true).length, 1); @@ -138,7 +138,7 @@ suite('Debug - Model', () => { const stoppedReason = 'breakpoint'; // Add the threads - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); session['raw'] = rawSession; @@ -225,7 +225,7 @@ suite('Debug - Model', () => { const runningThreadId = 2; const runningThreadName = 'runningThread'; const stoppedReason = 'breakpoint'; - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); session['raw'] = rawSession; @@ -338,7 +338,7 @@ suite('Debug - Model', () => { }); test('repl expressions', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); assert.equal(session.getReplElements().length, 0); model.addSession(session); @@ -362,7 +362,7 @@ suite('Debug - Model', () => { }); test('stack frame get specific source name', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); model.addSession(session); let firstStackFrame: StackFrame; @@ -393,7 +393,7 @@ suite('Debug - Model', () => { // Repl output test('repl output', () => { - const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined); + const session = new DebugSession({ resolved: { name: 'mockSession', type: 'node', request: 'launch' }, unresolved: undefined }, undefined, model, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined); const repl = new ReplModel(session); repl.appendToRepl('first line\n', severity.Error); repl.appendToRepl('second line', severity.Error); From 5e1df28b8fc3f4e5a18b04fef0b215e7cbec187c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 14 Jan 2019 13:23:20 -0800 Subject: [PATCH 0237/1066] Strict null check extensionTipsService --- src/tsconfig.strictNullChecks.json | 1 + .../electron-browser/extensionTipsService.ts | 32 ++++++++++++------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 09d49339198..2b8e88296e4 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -618,6 +618,7 @@ "./vs/workbench/parts/extensions/common/extensions.ts", "./vs/workbench/parts/extensions/common/extensionsFileTemplate.ts", "./vs/workbench/parts/extensions/common/extensionsInput.ts", + "./vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts", "./vs/workbench/parts/extensions/electron-browser/extensionsActions.ts", "./vs/workbench/parts/extensions/electron-browser/extensionsActivationProgress.ts", "./vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts", diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts index 12f655554a7..ca8b7f45b26 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionTipsService.ts @@ -311,7 +311,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe */ private fetchExtensionRecommendationContents(): Promise<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }[]> { const workspace = this.contextService.getWorkspace(); - return Promise.all<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource }>([ + return Promise.all<{ contents: IExtensionsConfigContent, source: ExtensionRecommendationSource } | null>([ this.resolveWorkspaceExtensionConfig(workspace).then(contents => contents ? { contents, source: workspace } : null), ...workspace.folders.map(workspaceFolder => this.resolveWorkspaceFolderExtensionConfig(workspaceFolder).then(contents => contents ? { contents, source: workspaceFolder } : null)) ]).then(contents => coalesce(contents)); @@ -638,7 +638,11 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return; } const id = recommendationsToSuggest[0]; - const name = caseInsensitiveGet(product.extensionImportantTips, id)['name']; + const entry = caseInsensitiveGet(product.extensionImportantTips, id); + if (!entry) { + return; + } + const name = entry['name']; // Indicates we have a suggested extension via the whitelist hasSuggestion = true; @@ -842,7 +846,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise { - let fetchPromise = Promise.resolve(null); + let fetchPromise = Promise.resolve(undefined); if (!this.proactiveRecommendationsFetched) { this.proactiveRecommendationsFetched = true; @@ -893,10 +897,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (!windowsPath || typeof windowsPath !== 'string') { return; } - windowsPath = windowsPath.replace('%USERPROFILE%', process.env['USERPROFILE']) - .replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)']) - .replace('%ProgramFiles%', process.env['ProgramFiles']) - .replace('%APPDATA%', process.env['APPDATA']); + windowsPath = windowsPath.replace('%USERPROFILE%', process.env['USERPROFILE']!) + .replace('%ProgramFiles(x86)%', process.env['ProgramFiles(x86)']!) + .replace('%ProgramFiles%', process.env['ProgramFiles']!) + .replace('%APPDATA%', process.env['APPDATA']!); promises.push(findExecutable(exeName, windowsPath)); } else { promises.push(findExecutable(exeName, paths.join('/usr/local/bin', exeName))); @@ -954,14 +958,17 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => { const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); if (!hashedRemotes.length) { - return null; + return undefined; } return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }, CancellationToken.None).then(context => { if (context.res.statusCode !== 200) { - return Promise.resolve(null); + return Promise.resolve(undefined); } return asJson(context).then((result) => { + if (!result) { + return; + } const allRecommendations: IDynamicWorkspaceRecommendations[] = Array.isArray(result['workspaceRecommendations']) ? result['workspaceRecommendations'] : []; if (!allRecommendations.length) { return; @@ -998,9 +1005,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private fetchExperimentalRecommendations() { this.experimentService.getExperimentsByType(ExperimentActionType.AddToRecommendations).then(experiments => { (experiments || []).forEach(experiment => { - if (experiment.state === ExperimentState.Run && experiment.action.properties && Array.isArray(experiment.action.properties.recommendations) && experiment.action.properties.recommendationReason) { - experiment.action.properties.recommendations.forEach(id => { - this._experimentalRecommendations[id] = experiment.action.properties.recommendationReason; + const action = experiment.action; + if (action && experiment.state === ExperimentState.Run && action.properties && Array.isArray(action.properties.recommendations) && action.properties.recommendationReason) { + action.properties.recommendations.forEach(id => { + this._experimentalRecommendations[id] = action.properties.recommendationReason; }); } }); From 15e5466ca8a7547aaa98a133a056eafec9de7b57 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 09:46:06 +0100 Subject: [PATCH 0238/1066] fixes #66529 --- src/vs/base/browser/ui/list/listView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 6b77d619d40..5623ac4c122 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -889,7 +889,8 @@ export class ListView implements ISpliceable, IDisposable { for (const range of renderRanges) { for (let i = range.start; i < range.end; i++) { - const beforeRow = i < this.items.length ? this.items[i + 1].row : null; + const afterIndex = i + 1; + const beforeRow = afterIndex < this.items.length ? this.items[afterIndex].row : null; const beforeElement = beforeRow ? beforeRow.domNode : null; this.insertItemInDOM(i, beforeElement); } From c96eabc53cc4103e05f90cde2a98a19166769a8e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 15 Jan 2019 09:55:10 +0100 Subject: [PATCH 0239/1066] fix #65024 --- src/vs/base/browser/ui/button/button.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/button/button.ts b/src/vs/base/browser/ui/button/button.ts index 9b7145f053c..c8b6ebbefaa 100644 --- a/src/vs/base/browser/ui/button/button.ts +++ b/src/vs/base/browser/ui/button/button.ts @@ -11,7 +11,7 @@ import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; import { Event as BaseEvent, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Gesture } from 'vs/base/browser/touch'; +import { Gesture, EventType } from 'vs/base/browser/touch'; export interface IButtonOptions extends IButtonStyles { title?: boolean; @@ -65,14 +65,16 @@ export class Button extends Disposable { Gesture.addTarget(this._element); - this._register(DOM.addDisposableListener(this._element, DOM.EventType.CLICK, e => { - if (!this.enabled) { - DOM.EventHelper.stop(e); - return; - } + [DOM.EventType.CLICK, EventType.Tap].forEach(eventType => { + this._register(DOM.addDisposableListener(this._element, eventType, e => { + if (!this.enabled) { + DOM.EventHelper.stop(e); + return; + } - this._onDidClick.fire(e); - })); + this._onDidClick.fire(e); + })); + }); this._register(DOM.addDisposableListener(this._element, DOM.EventType.KEY_DOWN, e => { const event = new StandardKeyboardEvent(e); From 4a460fb3cf076a79f6c8009493afc165c563a693 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 15 Jan 2019 10:35:49 +0100 Subject: [PATCH 0240/1066] Enrich the extension enablement changed event --- .../common/extensionEnablementService.ts | 35 ++++++++----------- .../common/extensionManagement.ts | 2 +- .../extensionEnablementService.test.ts | 4 +-- .../electron-browser/extensionsUtils.ts | 2 +- .../node/extensionsWorkbenchService.ts | 4 +-- .../electron-browser/extensionService.ts | 2 +- 6 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 3de0a1dbaf5..691bdc8c8b9 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, isIExtensionIdentifier, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, DidUninstallExtensionEvent, IExtensionEnablementService, IExtensionIdentifier, EnablementState, DidInstallExtensionEvent, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage'; @@ -21,8 +21,8 @@ export class ExtensionEnablementService extends Disposable implements IExtension _serviceBrand: any; - private _onEnablementChanged = new Emitter(); - public readonly onEnablementChanged: Event = this._onEnablementChanged.event; + private _onEnablementChanged = new Emitter(); + public readonly onEnablementChanged: Event = this._onEnablementChanged.event; private readonly storageManger: StorageManager; @@ -107,23 +107,14 @@ export class ExtensionEnablementService extends Disposable implements IExtension return true; } - setEnablement(arg: IExtension | IExtensionIdentifier, newState: EnablementState): Promise { - let identifier: IExtensionIdentifier; - if (isIExtensionIdentifier(arg)) { - identifier = arg; - } else { - if (!this.canChangeEnablement(arg)) { - return Promise.resolve(false); - } - identifier = arg.identifier; - } + setEnablement(extension: IExtension, newState: EnablementState): Promise { const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled; if (workspace && !this.hasWorkspace) { return Promise.reject(new Error(localize('noWorkspace', "No workspace."))); } - const currentState = this._getEnablementState(identifier); + const currentState = this._getEnablementState(extension.identifier); if (currentState === newState) { return Promise.resolve(false); @@ -132,20 +123,20 @@ export class ExtensionEnablementService extends Disposable implements IExtension switch (newState) { case EnablementState.Enabled: - this._enableExtension(identifier); + this._enableExtension(extension.identifier); break; case EnablementState.Disabled: - this._disableExtension(identifier); + this._disableExtension(extension.identifier); break; case EnablementState.WorkspaceEnabled: - this._enableExtensionInWorkspace(identifier); + this._enableExtensionInWorkspace(extension.identifier); break; case EnablementState.WorkspaceDisabled: - this._disableExtensionInWorkspace(identifier); + this._disableExtensionInWorkspace(extension.identifier); break; } - this._onEnablementChanged.fire(identifier); + this._onEnablementChanged.fire(extension); return Promise.resolve(true); } @@ -288,7 +279,9 @@ export class ExtensionEnablementService extends Disposable implements IExtension this.storageManger.set(storageId, extensions, scope); } - private onDidChangeStorage(extensions: IExtensionIdentifier[]): void { + private async onDidChangeStorage(extensionIdentifiers: IExtensionIdentifier[]): Promise { + const installedExtensions = await this.extensionManagementService.getInstalled(); + const extensions = installedExtensions.filter(installedExtension => extensionIdentifiers.some(identifier => areSameExtensions(identifier, installedExtension.identifier))); for (const extension of extensions) { this._onEnablementChanged.fire(extension); } @@ -299,7 +292,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension const wasDisabled = !this.isEnabled(event.local); this._reset(event.local.identifier); if (wasDisabled) { - this._onEnablementChanged.fire(event.local.identifier); + this._onEnablementChanged.fire(event.local); } } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 3d84049a84c..d87647aaa88 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -238,7 +238,7 @@ export interface IExtensionEnablementService { /** * Event to listen on for extension enablement changes */ - onEnablementChanged: Event; + onEnablementChanged: Event; /** * Returns all disabled extension identifiers for current workspace diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index f10411b4bbb..f3e08b06ba8 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -318,7 +318,7 @@ suite('ExtensionEnablementService Test', () => { await testObject.setEnablement(local, EnablementState.Disabled); return new Promise((c, e) => { testObject.onEnablementChanged(e => { - if (e.id === local.identifier.id) { + if (e.identifier.id === local.identifier.id) { c(); } }); @@ -356,7 +356,7 @@ suite('ExtensionEnablementService Test', () => { await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); return new Promise((c, e) => { testObject.onEnablementChanged(e => { - if (e.id === local.identifier.id) { + if (e.identifier.id === local.identifier.id) { c(); } }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index 22e10253789..bb382411969 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -106,7 +106,7 @@ export function onExtensionChanged(accessor: ServicesAccessor): Event e.identifier) .event, - extensionEnablementService.onEnablementChanged + Event.map(extensionEnablementService.onEnablementChanged, e => e.identifier) ), (list, id) => { if (!list) { return [id]; diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 0800d2a3af9..3fd9cd6f398 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -37,7 +37,7 @@ 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 } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest, ExtensionType, ExtensionIdentifierWithVersion, IExtension as IPlatformExtension } from 'vs/platform/extensions/common/extensions'; interface IExtensionStateProvider { (extension: Extension): T; @@ -978,7 +978,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this._onChange.fire(uninstalling); } - private onEnablementChanged(identifier: IExtensionIdentifier) { + private onEnablementChanged({ identifier }: IPlatformExtension) { const [extension] = this.local.filter(e => areSameExtensions(e.identifier, identifier)); if (extension && extension.local) { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 0fbe5780050..61cfddb59d0 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -114,7 +114,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } if (DYNAMIC_EXTENSION_POINTS) { - this._extensionEnablementService.onEnablementChanged((identifier) => { + this._extensionEnablementService.onEnablementChanged(({ identifier }) => { const extension = this._registry.getExtensionDescription(identifier.id); if (!extension) { // cannot handle enablement yet From c55f768dbe2bbb682abfd8702a0623b09c030c0b Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 14 Jan 2019 16:55:23 +0100 Subject: [PATCH 0241/1066] exploreer: do not clear foccus on setting editable --- .../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 c710b6b01fb..80da6da3100 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -164,7 +164,6 @@ export class ExplorerView extends ViewletPanel { let expandPromise = Promise.resolve(null); const isEditing = !!this.explorerService.getEditableData(e); if (isEditing) { - this.tree.setFocus([]); expandPromise = this.tree.expand(e.parent); } DOM.toggleClass(this.tree.getHTMLElement(), 'highlight', isEditing); From 71e65ca91db5ca7e119ed144f1a208594811d907 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 10:43:14 +0100 Subject: [PATCH 0242/1066] ctrl + L to clear console fixes #66302 --- .../parts/debug/electron-browser/debug.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index de65e213dbc..2b19d7a96b0 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -24,7 +24,7 @@ import { CallStackView } from 'vs/workbench/parts/debug/electron-browser/callSta import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA, - CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED + CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_IN_DEBUG_REPL } from 'vs/workbench/parts/debug/common/debug'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; @@ -149,7 +149,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(EnableAllBreakpointsAc registry.registerWorkbenchAction(new SyncActionDescriptor(DisableAllBreakpointsAction, DisableAllBreakpointsAction.ID, DisableAllBreakpointsAction.LABEL), 'Debug: Disable All Breakpoints', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusReplAction, FocusReplAction.ID, FocusReplAction.LABEL), 'Debug: Focus on Debug Console View', debugCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(SelectAndStartAction, SelectAndStartAction.ID, SelectAndStartAction.LABEL), 'Debug: Select and Start Debugging', debugCategory); -registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL), 'Debug: Clear Console', debugCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ClearReplAction, ClearReplAction.ID, ClearReplAction.LABEL, { primary: KeyMod.WinCtrl | KeyCode.KEY_L }, CONTEXT_IN_DEBUG_REPL), 'Debug: Clear Console', debugCategory); // Register Quick Open (Registry.as(QuickOpenExtensions.Quickopen)).registerQuickOpenHandler( From e5b73ab6cbdc776e6753556e1205d10378099b70 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 15 Jan 2019 10:47:04 +0100 Subject: [PATCH 0243/1066] Fix tests --- .../extensionEnablementService.test.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index f3e08b06ba8..d02b17a6687 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -12,7 +12,7 @@ import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/workbenchTestServices'; -import { IExtensionContributions, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IExtensionContributions, ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { isUndefinedOrNull } from 'vs/base/common/types'; function storageService(instantiationService: TestInstantiationService): IStorageService { @@ -87,7 +87,10 @@ suite('ExtensionEnablementService Test', () => { const target = sinon.spy(); testObject.onEnablementChanged(target); return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); + .then(() => { + assert.ok(target.calledOnce); + assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + }); }); test('test disable an extension globally again should return a falsy promise', () => { @@ -199,7 +202,10 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); + .then(() => { + assert.ok(target.calledOnce); + assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + }); }); test('test disable an extension globally and then for workspace', () => { @@ -220,7 +226,10 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); + .then(() => { + assert.ok(target.calledOnce); + assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + }); }); test('test disable an extension for workspace when there is no workspace throws error', () => { @@ -247,7 +256,10 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.a' }))); + .then(() => { + assert.ok(target.calledOnce); + assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + }); }); test('test enable an extension globally when already enabled return falsy promise', () => { @@ -273,7 +285,10 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) .then(() => testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceEnabled)) - .then(() => assert.ok(target.calledWithExactly({ id: 'pub.b' }))); + .then(() => { + assert.ok(target.calledOnce); + assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.b' }); + }); }); test('test enable an extension for workspace when already enabled return truthy promise', () => { From ec8b1907ad92f36100f39f6935834cb86692d4ba Mon Sep 17 00:00:00 2001 From: Asaf Cohen Date: Tue, 15 Jan 2019 12:37:22 +0200 Subject: [PATCH 0244/1066] 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 0245/1066] 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 f7c8db1d08e7e7c6e76c14ca9bf5aa8e469ad397 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 12:48:36 +0100 Subject: [PATCH 0246/1066] asyncDataTree: fix interesction computation and expand should await on correct refresh call --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index cf3972fc432..0a4c8f6d17d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -43,6 +43,10 @@ function isAncestor(ancestor: IAsyncDataTreeNode, descenda } } +function intersects(node: IAsyncDataTreeNode, other: IAsyncDataTreeNode): boolean { + return node === other || isAncestor(node, other) || isAncestor(other, node); +} + interface IDataTreeListTemplateData { templateData: T; } @@ -361,13 +365,13 @@ export class AsyncDataTree implements IDisposable return false; } - this.tree.expand(node === this.root ? null : node, recursive); + const result = this.tree.expand(node === this.root ? null : node, recursive); - if (node.state !== AsyncDataTreeNodeState.Loaded) { - await this.refreshNode(node, false); + if (node.state === AsyncDataTreeNodeState.Loading) { + await this.currentRefreshCalls.get(node)!; } - return true; + return result; } toggleCollapsed(element: T, recursive: boolean = false): boolean { @@ -507,7 +511,7 @@ export class AsyncDataTree implements IDisposable let result: Promise | undefined; this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { - if (isAncestor(refreshNode, node) || isAncestor(node, refreshNode)) { + if (intersects(refreshNode, node)) { result = refreshPromise.then(() => this.queueRefresh(node, recursive)); } }); From 6892cec1a3c0b695b2c3171ca3527d4440d02ede Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 12:49:35 +0100 Subject: [PATCH 0247/1066] explorer async await cleanup reaction on editable event --- .../electron-browser/views/explorerView.ts | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 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 80da6da3100..850055107f1 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -160,18 +160,21 @@ export class ExplorerView extends ViewletPanel { this.disposables.push(this.explorerService.onDidChangeRoots(() => this.setTreeInput())); this.disposables.push(this.explorerService.onDidChangeItem(e => this.refresh(e))); - this.disposables.push(this.explorerService.onDidChangeEditable(e => { - let expandPromise = Promise.resolve(null); + this.disposables.push(this.explorerService.onDidChangeEditable(async e => { const isEditing = !!this.explorerService.getEditableData(e); + if (isEditing) { - expandPromise = this.tree.expand(e.parent); + await this.tree.expand(e.parent); + } else { + DOM.removeClass(this.tree.getHTMLElement(), 'highlight'); + } + + await this.refresh(e.parent); + + if (isEditing) { + DOM.addClass(this.tree.getHTMLElement(), 'highlight'); + this.tree.reveal(e); } - DOM.toggleClass(this.tree.getHTMLElement(), 'highlight', isEditing); - expandPromise.then(() => this.refresh(e.parent)).then(() => { - if (isEditing) { - this.tree.reveal(e); - } - }); })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); From 6d6082a2b9b52b8286d15345784d0a671f53ed40 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 12:50:13 +0100 Subject: [PATCH 0248/1066] explorer: fix reacting on blur event when re-rendering tree --- .../electron-browser/views/explorerViewer.ts | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) 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 ffdd6257b14..b232d3ca572 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -12,7 +12,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IFileService, FileKind, IFileStat, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IFileLabelOptions, IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, TreeFilterResult, IAsyncDataSource, ITreeSorter, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; @@ -155,12 +155,11 @@ export class FilesRenderer implements ITreeRenderer { @@ -223,11 +228,17 @@ export class FilesRenderer implements ITreeRenderer { - done(inputBox.isInputValid(), true); + setTimeout(() => { + if (!ignoreBlur) { + done(inputBox.isInputValid(), true); + } + }, 0); }), label, styler ]; + + return toDisposable(() => ignoreBlur = true); } disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { From c1d4ea9d89887e58ecae1b10f87c55ea8c42f96f Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 12:50:36 +0100 Subject: [PATCH 0249/1066] explorer: always resolve parent when adding new file folder to it fixes #66484 --- .../files/electron-browser/fileActions.ts | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 918c4f35be5..77513d81495 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -127,28 +127,28 @@ export class NewFileAction extends BaseErrorReportingAction { } const stat = new ExplorerItem(PLACEHOLDER_URI, folder, false); - folder.addChild(stat); + return folder.fetchChildren(this.fileService).then(() => { + folder.addChild(stat); - const onSuccess = value => { - return this.fileService.createFile(resources.joinPath(folder.resource, value)).then(stat => { - return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); - }, (error) => { - this.onErrorWithRetry(error, () => onSuccess(value)); - }); - }; + const onSuccess = value => { + return this.fileService.createFile(resources.joinPath(folder.resource, value)).then(stat => { + return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); + }, (error) => { + this.onErrorWithRetry(error, () => onSuccess(value)); + }); + }; - this.explorerService.setEditable(stat, { - validationMessage: value => validateFileName(stat, value), - onFinish: (value, success) => { - folder.removeChild(stat); - this.explorerService.setEditable(stat, null); - if (success) { - onSuccess(value); + this.explorerService.setEditable(stat, { + validationMessage: value => validateFileName(stat, value), + onFinish: (value, success) => { + folder.removeChild(stat); + this.explorerService.setEditable(stat, null); + if (success) { + onSuccess(value); + } } - } + }); }); - - return Promise.resolve(null); } } @@ -181,28 +181,28 @@ export class NewFolderAction extends BaseErrorReportingAction { } const stat = new ExplorerItem(PLACEHOLDER_URI, folder, true); - folder.addChild(stat); + return folder.fetchChildren(this.fileService).then(() => { + folder.addChild(stat); - const onSuccess = value => { - return this.fileService.createFolder(resources.joinPath(folder.resource, value)).then(stat => { - return this.explorerService.select(stat.resource, true); - }, (error) => { - this.onErrorWithRetry(error, () => onSuccess(value)); - }); - }; + const onSuccess = value => { + return this.fileService.createFolder(resources.joinPath(folder.resource, value)).then(stat => { + return this.explorerService.select(stat.resource, true); + }, (error) => { + this.onErrorWithRetry(error, () => onSuccess(value)); + }); + }; - this.explorerService.setEditable(stat, { - validationMessage: value => validateFileName(stat, value), - onFinish: (value, success) => { - folder.removeChild(stat); - this.explorerService.setEditable(stat, null); - if (success) { - onSuccess(value); + this.explorerService.setEditable(stat, { + validationMessage: value => validateFileName(stat, value), + onFinish: (value, success) => { + folder.removeChild(stat); + this.explorerService.setEditable(stat, null); + if (success) { + onSuccess(value); + } } - } + }); }); - - return Promise.resolve(null); } } From cced5e85549d8e3d401bcf0ba379f9ffcb7d5163 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 12:14:34 +0100 Subject: [PATCH 0250/1066] Add support for enabling/installing extensions --- .../cachedExtensionScanner.ts | 239 ++++++++++-------- .../electron-browser/extensionService.ts | 121 +++++++-- .../node/extensionDescriptionRegistry.ts | 5 + .../extensions/node/extensionPoints.ts | 10 +- 4 files changed, 237 insertions(+), 138 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index ed093e088d9..29cbfd65c41 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -50,6 +50,7 @@ export class CachedExtensionScanner { public readonly scannedExtensions: Promise; private _scannedExtensionsResolve: (result: IExtensionDescription[]) => void; private _scannedExtensionsReject: (err: any) => void; + public readonly translationConfig: Promise; constructor( @INotificationService private readonly _notificationService: INotificationService, @@ -61,42 +62,58 @@ export class CachedExtensionScanner { this._scannedExtensionsResolve = resolve; this._scannedExtensionsReject = reject; }); + this.translationConfig = CachedExtensionScanner._readTranslationConfig(); } - public startScanningExtensions(log: ILog): void { - CachedExtensionScanner._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log) - .then(({ system, user, development }) => { - let result = new Map(); - system.forEach((systemExtension) => { - const extensionKey = ExtensionIdentifier.toKey(systemExtension.identifier); - const extension = result.get(extensionKey); - if (extension) { - log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, systemExtension.extensionLocation.fsPath)); - } - result.set(extensionKey, systemExtension); - }); - user.forEach((userExtension) => { - const extensionKey = ExtensionIdentifier.toKey(userExtension.identifier); - const extension = result.get(extensionKey); - if (extension) { - log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); - } - result.set(extensionKey, userExtension); - }); - development.forEach(developedExtension => { - log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); - const extensionKey = ExtensionIdentifier.toKey(developedExtension.identifier); - const extension = result.get(extensionKey); - if (extension) { - log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); - } - result.set(extensionKey, developedExtension); - }); - let r: IExtensionDescription[] = []; - result.forEach((value) => r.push(value)); - return r; - }) - .then(this._scannedExtensionsResolve, this._scannedExtensionsReject); + public async scanSingleExtension(path: string, isBuiltin: boolean, log: ILog): Promise { + const translations = await this.translationConfig; + + const version = pkg.version; + const commit = product.commit; + const devMode = !!process.env['VSCODE_DEV']; + const locale = platform.locale; + const input = new ExtensionScannerInput(version, commit, locale, devMode, path, isBuiltin, false, translations); + return ExtensionScanner.scanSingleExtension(input, log); + } + + public async startScanningExtensions(log: ILog): Promise { + try { + const translations = await this.translationConfig; + const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log, translations); + + let result = new Map(); + system.forEach((systemExtension) => { + const extensionKey = ExtensionIdentifier.toKey(systemExtension.identifier); + const extension = result.get(extensionKey); + if (extension) { + log.warn(systemExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, systemExtension.extensionLocation.fsPath)); + } + result.set(extensionKey, systemExtension); + }); + user.forEach((userExtension) => { + const extensionKey = ExtensionIdentifier.toKey(userExtension.identifier); + const extension = result.get(extensionKey); + if (extension) { + log.warn(userExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); + } + result.set(extensionKey, userExtension); + }); + development.forEach(developedExtension => { + log.info('', nls.localize('extensionUnderDevelopment', "Loading development extension at {0}", developedExtension.extensionLocation.fsPath)); + const extensionKey = ExtensionIdentifier.toKey(developedExtension.identifier); + const extension = result.get(extensionKey); + if (extension) { + log.warn(developedExtension.extensionLocation.fsPath, nls.localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, developedExtension.extensionLocation.fsPath)); + } + result.set(extensionKey, developedExtension); + }); + let r: IExtensionDescription[] = []; + result.forEach((value) => r.push(value)); + + this._scannedExtensionsResolve(r); + } catch (err) { + this._scannedExtensionsReject(err); + } } private static async _validateExtensionsCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): Promise { @@ -209,86 +226,90 @@ export class CachedExtensionScanner { return result; } - private static _scanInstalledExtensions(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, extensionEnablementService: IExtensionEnablementService, log: ILog): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { - - const translationConfig: Promise = platform.translationsConfigFile - ? pfs.readFile(platform.translationsConfigFile, 'utf8').then((content) => { - try { - return JSON.parse(content) as Translations; - } catch (err) { - return Object.create(null); - } - }, (err) => { - return Object.create(null); - }) - : Promise.resolve(Object.create(null)); - - return translationConfig.then((translations) => { - const version = pkg.version; - const commit = product.commit; - const devMode = !!process.env['VSCODE_DEV']; - const locale = platform.locale; - - const builtinExtensions = this._scanExtensionsWithCache( - windowService, - notificationService, - environmentService, - BUILTIN_MANIFEST_CACHE_FILE, - new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations), - log - ); - - let finalBuiltinExtensions: Promise = builtinExtensions; - - if (devMode) { - const builtInExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); - const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8') - .then(raw => JSON.parse(raw)); - - const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); - const controlFile = pfs.readFile(controlFilePath, 'utf8') - .then(raw => JSON.parse(raw), () => ({} as any)); - - const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations); - const extraBuiltinExtensions = Promise.all([builtInExtensions, controlFile]) - .then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control)) - .then(resolver => ExtensionScanner.scanExtensions(input, log, resolver)); - - finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions); + private static async _readTranslationConfig(): Promise { + if (platform.translationsConfigFile) { + try { + const content = await pfs.readFile(platform.translationsConfigFile, 'utf8'); + return JSON.parse(content) as Translations; + } catch (err) { + // no problemo } + } + return Object.create(null); + } - const userExtensions = ( - extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath - ? Promise.resolve([]) - : this._scanExtensionsWithCache( - windowService, - notificationService, - environmentService, - USER_MANIFEST_CACHE_FILE, - new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), - log - ) + private static _scanInstalledExtensions( + windowService: IWindowService, + notificationService: INotificationService, + environmentService: IEnvironmentService, + extensionEnablementService: IExtensionEnablementService, + log: ILog, + translations: Translations + ): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { + + const version = pkg.version; + const commit = product.commit; + const devMode = !!process.env['VSCODE_DEV']; + const locale = platform.locale; + + const builtinExtensions = this._scanExtensionsWithCache( + windowService, + notificationService, + environmentService, + BUILTIN_MANIFEST_CACHE_FILE, + new ExtensionScannerInput(version, commit, locale, devMode, getSystemExtensionsRoot(), true, false, translations), + log + ); + + let finalBuiltinExtensions: Promise = builtinExtensions; + + if (devMode) { + const builtInExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); + const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8') + .then(raw => JSON.parse(raw)); + + const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); + const controlFile = pfs.readFile(controlFilePath, 'utf8') + .then(raw => JSON.parse(raw), () => ({} as any)); + + const input = new ExtensionScannerInput(version, commit, locale, devMode, getExtraDevSystemExtensionsRoot(), true, false, translations); + const extraBuiltinExtensions = Promise.all([builtInExtensions, controlFile]) + .then(([builtInExtensions, control]) => new ExtraBuiltInExtensionResolver(builtInExtensions, control)) + .then(resolver => ExtensionScanner.scanExtensions(input, log, resolver)); + + finalBuiltinExtensions = ExtensionScanner.mergeBuiltinExtensions(builtinExtensions, extraBuiltinExtensions); + } + + const userExtensions = ( + extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath + ? Promise.resolve([]) + : this._scanExtensionsWithCache( + windowService, + notificationService, + environmentService, + USER_MANIFEST_CACHE_FILE, + new ExtensionScannerInput(version, commit, locale, devMode, environmentService.extensionsPath, false, false, translations), + log + ) + ); + + // Always load developed extensions while extensions development + let developedExtensions: Promise = Promise.resolve([]); + if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) { + developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions( + new ExtensionScannerInput(version, commit, locale, devMode, fsPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log ); + } - // Always load developed extensions while extensions development - let developedExtensions: Promise = Promise.resolve([]); - if (environmentService.isExtensionDevelopment && environmentService.extensionDevelopmentLocationURI && environmentService.extensionDevelopmentLocationURI.scheme === Schemas.file) { - developedExtensions = ExtensionScanner.scanOneOrMultipleExtensions( - new ExtensionScannerInput(version, commit, locale, devMode, fsPath(environmentService.extensionDevelopmentLocationURI), false, true, translations), log - ); - } - - return Promise.all([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { - const system = extensionDescriptions[0]; - const user = extensionDescriptions[1]; - const development = extensionDescriptions[2]; - return { system, user, development }; - }).then(undefined, err => { - log.error('', err); - return { system: [], user: [], development: [] }; - }); + return Promise.all([finalBuiltinExtensions, userExtensions, developedExtensions]).then((extensionDescriptions: IExtensionDescription[][]) => { + const system = extensionDescriptions[0]; + const user = extensionDescriptions[1]; + const development = extensionDescriptions[2]; + return { system, user, development }; + }).then(undefined, err => { + log.error('', err); + return { system: [], user: [], development: [] }; }); - } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 61cfddb59d0..51dcf898d48 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -13,7 +13,7 @@ import * as perf from 'vs/base/common/performance'; import { isEqualOrParent } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnablementState, IExtensionEnablementService, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { EnablementState, IExtensionEnablementService, IExtensionIdentifier, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { BetterMergeId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -29,7 +29,8 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/n import { ResponsiveState } from 'vs/workbench/services/extensions/node/rpcProtocol'; import { CachedExtensionScanner, Logger } from 'vs/workbench/services/extensions/electron-browser/cachedExtensionScanner'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/electron-browser/extensionHostProcessManager'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtensionIdentifier, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { Schemas } from 'vs/base/common/network'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); @@ -85,6 +86,7 @@ export class ExtensionService extends Disposable implements IExtensionService { @IEnvironmentService private readonly _environmentService: IEnvironmentService, @ITelemetryService private readonly _telemetryService: ITelemetryService, @IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService, + @IExtensionManagementService private readonly _extensionManagementService: IExtensionManagementService, @IWindowService private readonly _windowService: IWindowService, @ILifecycleService private readonly _lifecycleService: ILifecycleService ) { @@ -114,32 +116,56 @@ export class ExtensionService extends Disposable implements IExtensionService { } if (DYNAMIC_EXTENSION_POINTS) { - this._extensionEnablementService.onEnablementChanged(({ identifier }) => { - const extension = this._registry.getExtensionDescription(identifier.id); - if (!extension) { - // cannot handle enablement yet - return; + this._extensionEnablementService.onEnablementChanged((extension) => { + if (this._extensionEnablementService.isEnabled(extension)) { + // an extension has been enabled + this._addExtension(extension); + } else { + // an extension has been disabled + this._removeExtension(extension.identifier.id); } + }); - // TODO@Alex: Assuming the extension becomes disabled - // until the enablement service gives better API - this._removeExtension(extension); + this._extensionManagementService.onDidInstallExtension((event) => { + if (event.local) { + // an extension has been installed + this._addExtension(event.local); + } + }); + + this._extensionManagementService.onDidUninstallExtension((event) => { + if (!event.error) { + // an extension has been uninstalled + this._removeExtension(event.identifier.id); + } }); } } - private _removeExtension(extension: IExtensionDescription): boolean { - if (!this._canRemoveExtension(extension)) { - return false; + private _removeExtension(extensionId: string): void { + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + // ignore disabling an extension which is not running + return; } - this._registry.remove(extension.identifier); + if (!this._canRemoveExtension(extensionDescription)) { + // uses non-dynamic extension point or is activated + return; + } + + // Remove the extension from the local registry and from the extension host + this._registry.remove(extensionDescription.identifier); // TODO@Alex: remove from the extension host + this._rehandleExtensionPoints(extensionDescription); + } + + private _rehandleExtensionPoints(extensionDescription: IExtensionDescription): void { const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); - if (extension.contributes) { - for (let extPointName in extension.contributes) { - if (hasOwnProperty.call(extension.contributes, extPointName)) { + if (extensionDescription.contributes) { + for (let extPointName in extensionDescription.contributes) { + if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { affectedExtensionPoints[extPointName] = true; } } @@ -155,15 +181,9 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - return true; } - private _canRemoveExtension(extension: IExtensionDescription): boolean { - if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { - // Extension is running, cannot remove it safely - return false; - } - + private _usesOnlyDynamicExtensionPoints(extension: IExtensionDescription): boolean { const extensionPoints = ExtensionsRegistry.getExtensionPointsMap(); if (extension.contributes) { for (let extPointName in extension.contributes) { @@ -179,6 +199,36 @@ export class ExtensionService extends Disposable implements IExtensionService { return true; } + private _canRemoveExtension(extension: IExtensionDescription): boolean { + if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { + // Extension is running, cannot remove it safely + return false; + } + + return this._usesOnlyDynamicExtensionPoints(extension); + } + + private async _addExtension(extension: IExtension): Promise { + if (this._windowService.getConfiguration().remoteAuthority) { + return; + } + if (extension.location.scheme !== Schemas.file) { + return; + } + + const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); + if (!extensionDescription || !this._usesOnlyDynamicExtensionPoints(extensionDescription)) { + // uses non-dynamic extension point + return; + } + + // Add the extension to the local registry and to the extension host + this._registry.add(extensionDescription); + // TODO@Alex: add to the extension host + + this._rehandleExtensionPoints(extensionDescription); + } + private _startDelayed(lifecycleService: ILifecycleService): void { // delay extension host creation and extension scanning // until the workbench is running. we cannot defer the @@ -238,7 +288,18 @@ export class ExtensionService extends Disposable implements IExtensionService { private _startExtensionHostProcess(isInitialStart: boolean, initialActivationEvents: string[]): void { this._stopExtensionHostProcess(); - const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, !isInitialStart, this.getExtensions(), this._extensionHostLogsLocation); + let autoStart: boolean; + let extensions: Promise; + if (isInitialStart) { + autoStart = false; + extensions = this._extensionScanner.scannedExtensions; + } else { + // restart case + autoStart = true; + extensions = this.getExtensions(); + } + + const extHostProcessWorker = this._instantiationService.createInstance(ExtensionHostProcessWorker, autoStart, extensions, this._extensionHostLogsLocation); const extHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, extHostProcessWorker, null, initialActivationEvents); extHostProcessManager.onDidCrash(([code, signal]) => this._onExtensionHostCrashed(code, signal)); extHostProcessManager.onDidChangeResponsiveState((responsiveState) => { this._onDidChangeResponsiveChange.fire({ target: extHostProcessManager, isResponsive: responsiveState === ResponsiveState.Responsive }); }); @@ -399,14 +460,18 @@ export class ExtensionService extends Disposable implements IExtensionService { // --- impl - private async _scanAndHandleExtensions(): Promise { - this._extensionScanner.startScanningExtensions(new Logger((severity, source, message) => { + private createLogger(): Logger { + return new Logger((severity, source, message) => { if (this._isDev && source) { this._logOrShowMessage(severity, `[${source}]: ${message}`); } else { this._logOrShowMessage(severity, message); } - })); + }); + } + + private async _scanAndHandleExtensions(): Promise { + this._extensionScanner.startScanningExtensions(this.createLogger()); const extensionHost = this._extensionHostProcessManagers[0]; const extensions = await this._extensionScanner.scannedExtensions; diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index 506e26738de..fc345fcc52c 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -60,6 +60,11 @@ export class ExtensionDescriptionRegistry { this._initialize(); } + public add(extension: IExtensionDescription): void { + this._extensionDescriptions.push(extension); + this._initialize(); + } + public containsActivationEvent(activationEvent: string): boolean { return this._activationMap.has(activationEvent); } diff --git a/src/vs/workbench/services/extensions/node/extensionPoints.ts b/src/vs/workbench/services/extensions/node/extensionPoints.ts index 34af4222221..26b4f26d07d 100644 --- a/src/vs/workbench/services/extensions/node/extensionPoints.ts +++ b/src/vs/workbench/services/extensions/node/extensionPoints.ts @@ -506,7 +506,7 @@ export class ExtensionScanner { /** * Read the extension defined in `absoluteFolderPath` */ - public static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise { + private static scanExtension(version: string, log: ILog, absoluteFolderPath: string, isBuiltin: boolean, isUnderDevelopment: boolean, nlsConfig: NlsConfiguration): Promise { absoluteFolderPath = path.normalize(absoluteFolderPath); let parser = new ExtensionManifestParser(version, log, absoluteFolderPath, isBuiltin, isUnderDevelopment); @@ -609,6 +609,14 @@ export class ExtensionScanner { }); } + public static scanSingleExtension(input: ExtensionScannerInput, log: ILog): Promise { + const absoluteFolderPath = input.absoluteFolderPath; + const isBuiltin = input.isBuiltin; + const isUnderDevelopment = input.isUnderDevelopment; + const nlsConfig = ExtensionScannerInput.createNLSConfig(input); + return this.scanExtension(input.ourVersion, log, absoluteFolderPath, isBuiltin, isUnderDevelopment, nlsConfig); + } + public static mergeBuiltinExtensions(builtinExtensions: Promise, extraBuiltinExtensions: Promise): Promise { return Promise.all([builtinExtensions, extraBuiltinExtensions]).then(([builtinExtensions, extraBuiltinExtensions]) => { let resultMap: { [id: string]: IExtensionDescription; } = Object.create(null); From f7932f5bc5a17847a0a3ff03608789241d3dec83 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 12:32:09 +0100 Subject: [PATCH 0251/1066] Send a delta of the added/removed extensions to the extension points handlers --- .../extensions/common/extensionsRegistry.ts | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index e9187014ccb..d37aabe470a 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -11,6 +11,7 @@ import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/co import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { IExtensionDescription, IMessage } from 'vs/workbench/services/extensions/common/extensions'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const hasOwnProperty = Object.hasOwnProperty; const schemaRegistry = Registry.as(Extensions.JSONContribution); @@ -60,7 +61,7 @@ export interface IExtensionPointUser { } export interface IExtensionPointHandler { - (extensions: IExtensionPointUser[]): void; + (extensions: IExtensionPointUser[], delta: ExtensionPointUserDelta): void; } export interface IExtensionPoint { @@ -68,6 +69,39 @@ export interface IExtensionPoint { setHandler(handler: IExtensionPointHandler): void; } +export class ExtensionPointUserDelta { + + private static _toSet(arr: IExtensionPointUser[]): Set { + const result = new Set(); + for (let i = 0, len = arr.length; i < len; i++) { + result.add(ExtensionIdentifier.toKey(arr[i].description.identifier)); + } + return result; + } + + public static compute(previous: IExtensionPointUser[] | null, current: IExtensionPointUser[]): ExtensionPointUserDelta { + if (!previous || !previous.length) { + return new ExtensionPointUserDelta(current, []); + } + if (!current || !current.length) { + return new ExtensionPointUserDelta([], current); + } + + const previousSet = this._toSet(previous); + const currentSet = this._toSet(current); + + let added = current.filter(user => !previousSet.has(ExtensionIdentifier.toKey(user.description.identifier))); + let removed = previous.filter(user => !currentSet.has(ExtensionIdentifier.toKey(user.description.identifier))); + + return new ExtensionPointUserDelta(added, removed); + } + + constructor( + public readonly added: IExtensionPointUser[], + public readonly removed: IExtensionPointUser[], + ) { } +} + export class ExtensionPoint implements IExtensionPoint { public readonly name: string; @@ -76,6 +110,7 @@ export class ExtensionPoint implements IExtensionPoint { private _handler: IExtensionPointHandler | null; private _handlerCalled: boolean; private _users: IExtensionPointUser[] | null; + private _delta: ExtensionPointUserDelta | null; constructor(name: string, isDynamic: boolean) { this.name = name; @@ -83,6 +118,7 @@ export class ExtensionPoint implements IExtensionPoint { this._handler = null; this._handlerCalled = false; this._users = null; + this._delta = null; } setHandler(handler: IExtensionPointHandler): void { @@ -94,6 +130,7 @@ export class ExtensionPoint implements IExtensionPoint { } acceptUsers(users: IExtensionPointUser[]): void { + this._delta = ExtensionPointUserDelta.compute(this._users, users); this._users = users; this._handle(); } @@ -103,16 +140,13 @@ export class ExtensionPoint implements IExtensionPoint { return; } - let users = this._users; - this._users = null; - if (this._handlerCalled && !this.isDynamic) { throw new Error('The extension point is not dynamic!'); } try { this._handlerCalled = true; - this._handler(users); + this._handler(this._users, this._delta); } catch (err) { onUnexpectedError(err); } From 471a786cf3b0e1c9eeed99321ed46e164d4025ab Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 13:50:13 +0100 Subject: [PATCH 0252/1066] Activate newly installed/enabled extensions --- src/vs/workbench/api/node/extHost.protocol.ts | 3 ++ .../api/node/extHostExtensionService.ts | 13 ++++++ .../extensionHostProcessManager.ts | 12 +++++- .../electron-browser/extensionService.ts | 42 +++++++++++++++++-- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d6341733d16..a408ee583e7 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -739,6 +739,9 @@ export interface ExtHostExtensionServiceShape { $resolveAuthority(remoteAuthority: string): Promise; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise; $activateByEvent(activationEvent: string): Promise; + $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise; + + $addExtension(extension: IExtensionDescription): Promise; $test_latency(n: number): Promise; $test_up(b: Buffer): Promise; diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index c4b551ccd29..b9b2916c111 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -662,6 +662,19 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { ); } + public $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise { + return ( + this._barrier.wait() + .then(_ => this._activateById(extensionId, new ExtensionActivatedByEvent(false, activationEvent))) + ); + } + + public $addExtension(extension: IExtensionDescription): Promise { + (extension).extensionLocation = URI.revive(extension.extensionLocation); + this._registry.add(extension); + return Promise.resolve(undefined); + } + public async $test_latency(n: number): Promise { return n; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 753b4782f1c..e55bd38105d 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -12,7 +12,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ExtHostCustomersRegistry } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostExtensionServiceShape, IExtHostContext, MainContext } from 'vs/workbench/api/node/extHost.protocol'; -import { ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; +import { ProfileSession, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; import { ProxyIdentifier } from 'vs/workbench/services/extensions/node/proxyIdentifier'; @@ -199,6 +199,12 @@ export class ExtensionHostProcessManager extends Disposable { return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService); } + public activate(extension: ExtensionIdentifier, activationEvent: string): Promise { + return this._extensionHostProcessProxy.then((proxy) => { + return proxy.value.$activate(extension, activationEvent); + }); + } + public activateByEvent(activationEvent: string): Promise { if (this._extensionHostProcessFinishedActivateEvents[activationEvent] || !this._extensionHostProcessProxy) { return NO_OP_VOID_PROMISE; @@ -242,6 +248,10 @@ export class ExtensionHostProcessManager extends Disposable { public start(enabledExtensionIds: ExtensionIdentifier[]): Promise { return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds)); } + + public addExtension(extension: IExtensionDescription): Promise { + return this._extensionHostProcessProxy.then(proxy => proxy.value.$addExtension(extension)); + } } const colorTables = [ diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 51dcf898d48..0d047f46657 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -224,9 +224,43 @@ export class ExtensionService extends Disposable implements IExtensionService { // Add the extension to the local registry and to the extension host this._registry.add(extensionDescription); - // TODO@Alex: add to the extension host this._rehandleExtensionPoints(extensionDescription); + + if (this._extensionHostProcessManagers.length > 0) { + await this._extensionHostProcessManagers[0].addExtension(extensionDescription); + } + + let shouldActivate = false; + let shouldActivateReason: string | null = null; + if (Array.isArray(extensionDescription.activationEvents)) { + for (let activationEvent of extensionDescription.activationEvents) { + // TODO@joao: there's no easy way to contribute this + if (activationEvent === 'onUri') { + activationEvent = `onUri:${ExtensionIdentifier.toKey(extensionDescription.identifier)}`; + } + + if (this._allRequestedActivateEvents[activationEvent]) { + // This activation event was fired before the extension was added + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + + if (activationEvent === '*') { + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } + } + } + + // TODO@Alex: workspaceContains will not work + if (shouldActivate) { + await Promise.all( + this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason)) + ).then(() => { }); + } } private _startDelayed(lifecycleService: ILifecycleService): void { @@ -350,14 +384,14 @@ export class ExtensionService extends Disposable implements IExtensionService { if (this._installedExtensionsReady.isOpen()) { // Extensions have been scanned and interpreted + // Record the fact that this activationEvent was requested (in case of a restart) + this._allRequestedActivateEvents[activationEvent] = true; + if (!this._registry.containsActivationEvent(activationEvent)) { // There is no extension that is interested in this activation event return NO_OP_VOID_PROMISE; } - // Record the fact that this activationEvent was requested (in case of a restart) - this._allRequestedActivateEvents[activationEvent] = true; - return this._activateByEvent(activationEvent); } else { // Extensions have not been scanned yet. From e41b8290e52a69f345d130a6471e56f2bb2fa757 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 13:51:03 +0100 Subject: [PATCH 0253/1066] Fix strict null check --- .../workbench/services/extensions/common/extensionsRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index d37aabe470a..029a08153ba 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -136,7 +136,7 @@ export class ExtensionPoint implements IExtensionPoint { } private _handle(): void { - if (this._handler === null || this._users === null) { + if (this._handler === null || this._users === null || this._delta === null) { return; } From f6a7ab12a67cc7e61e70663131780d04a22bfd37 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 11:00:30 +0100 Subject: [PATCH 0254/1066] Revert "remove children resolution event" This reverts commit 91e42bf266a61113920178673fb415fb051ed17a. --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 28 +++++++++++++------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 0a4c8f6d17d..c66a021bee3 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -130,6 +130,16 @@ function asTreeContextMenuEvent(e: ITreeContextMenuEvent { + readonly element: T | null; + readonly reason: ChildrenResolutionReason; +} + function asAsyncDataTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { const nodes = (data as ElementsDragAndDropData>).elements; @@ -342,7 +352,7 @@ export class AsyncDataTree implements IDisposable throw new Error('Tree input not set'); } - return this.refreshNode(this.getDataNode(element), recursive); + return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh); } // Tree @@ -492,17 +502,17 @@ export class AsyncDataTree implements IDisposable return node; } - private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean): Promise { - await this.queueRefresh(node, recursive); + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { + await this.queueRefresh(node, recursive, reason); if (recursive && node.children) { - await Promise.all(node.children.map(child => this.refreshNode(child, recursive))); + await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason))); } } private currentRefreshCalls = new Map, Promise>(); - private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean): Promise { + private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { if (node.state === AsyncDataTreeNodeState.Disposed) { console.error('Async data tree node is disposed'); return; @@ -512,7 +522,7 @@ export class AsyncDataTree implements IDisposable this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { if (intersects(refreshNode, node)) { - result = refreshPromise.then(() => this.queueRefresh(node, recursive)); + result = refreshPromise.then(() => this.queueRefresh(node, recursive, reason)); } }); @@ -520,13 +530,13 @@ export class AsyncDataTree implements IDisposable return result; } - result = this.doRefresh(node, recursive); + result = this.doRefresh(node, recursive, reason); this.currentRefreshCalls.set(node, result); return always(result, () => this.currentRefreshCalls.delete(node)); } - private doRefresh(node: IAsyncDataTreeNode, recursive: boolean): Promise { + private doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); if (!hasChildren) { @@ -588,7 +598,7 @@ export class AsyncDataTree implements IDisposable if (deep) { this.collapse(node.element.element as T); } else { - this.refreshNode(node.element, false); + this.refreshNode(node.element, false, ChildrenResolutionReason.Expand); } } } From 91ceb9d4c65173f4f30fba9c563d17d76be1ae9b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 15:22:35 +0100 Subject: [PATCH 0255/1066] tree.autoExpandSingleChildren fixes #64888 --- src/vs/base/browser/ui/tree/abstractTree.ts | 5 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 82 +++++++++++-------- src/vs/base/browser/ui/tree/indexTreeModel.ts | 44 ++++++++-- .../base/browser/ui/tree/objectTreeModel.ts | 2 +- .../electron-browser/views/explorerView.ts | 3 +- 5 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 65c8b83ef84..640ca169306 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -277,9 +277,10 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IListOptions { - collapseByDefault?: boolean; // defaults to false - filter?: ITreeFilter; + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; + readonly autoExpandSingleChildren?: boolean; } export abstract class AbstractTree implements IDisposable { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c66a021bee3..a02ddccef81 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -183,6 +183,7 @@ class AsyncDataTreeNodeListDragAndDrop implements IListDragAndDrop(options?: IAsyncDataTreeOptions): IObjectTreeOptions, TFilterData> | undefined { return options && { ...options, + collapseByDefault: true, identityProvider: options.identityProvider && { getId(el) { return options.identityProvider!.getId(el.element as T); @@ -230,6 +231,7 @@ function asTreeElement(node: IAsyncDataTreeNode): ITreeEle export interface IAsyncDataTreeOptions extends IAbstractTreeOptions { identityProvider?: IIdentityProvider; sorter?: ITreeSorter; + autoExpandSingleChildren?: boolean; } export class AsyncDataTree implements IDisposable { @@ -239,6 +241,7 @@ export class AsyncDataTree implements IDisposable private readonly nodes = new Map>(); private readonly refreshPromises = new Map, CancelablePromise>(); private readonly identityProvider?: IIdentityProvider; + private readonly autoExpandSingleChildren: boolean; private readonly _onDidChangeNodeState = new Emitter>(); @@ -261,14 +264,14 @@ export class AsyncDataTree implements IDisposable delegate: IListVirtualDelegate, renderers: ITreeRenderer[], private dataSource: IAsyncDataSource, - options?: IAsyncDataTreeOptions + options: IAsyncDataTreeOptions = {} ) { - this.identityProvider = options && options.identityProvider; + this.identityProvider = options.identityProvider; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; const objectTreeDelegate = new ComposedTreeDelegate>(delegate); const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); const objectTreeOptions = asObjectTreeOptions(options) || {}; - objectTreeOptions.collapseByDefault = true; this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); @@ -371,7 +374,7 @@ export class AsyncDataTree implements IDisposable async expand(element: T, recursive: boolean = false): Promise { const node = this.getDataNode(element); - if (!this.tree.isCollapsed(node === this.root ? null : node)) { + if (node !== this.root && !this.tree.isCollapsed(node === this.root ? null : node)) { return false; } @@ -536,48 +539,59 @@ export class AsyncDataTree implements IDisposable return always(result, () => this.currentRefreshCalls.delete(node)); } - private doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { + private async doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); if (!hasChildren) { this.setChildren(node, [], recursive); - return Promise.resolve(); - } else if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) { + return; + } + + if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) { node.state = AsyncDataTreeNodeState.Uninitialized; - return Promise.resolve(); - } else { - node.state = AsyncDataTreeNodeState.Loading; + return; + } + + node.state = AsyncDataTreeNodeState.Loading; + this._onDidChangeNodeState.fire(node); + + const slowTimeout = timeout(800); + + slowTimeout.then(() => { + node.state = AsyncDataTreeNodeState.Slow; + this._onDidChangeNodeState.fire(node); + }, _ => null); + + try { + const children = await this.doGetChildren(node); + slowTimeout.cancel(); + node.state = AsyncDataTreeNodeState.Loaded; this._onDidChangeNodeState.fire(node); - const slowTimeout = timeout(800); + this.setChildren(node, children, recursive); - slowTimeout.then(() => { - node.state = AsyncDataTreeNodeState.Slow; - this._onDidChangeNodeState.fire(node); - }, _ => null); + if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { + const treeNode = this.tree.getNode(node); + const visibleChildren = treeNode.children.filter(node => node.visible); - return Promise.resolve(this.doGetChildren(node)) - .then(children => { - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Loaded; - this._onDidChangeNodeState.fire(node); + if (visibleChildren.length === 1) { + this.tree.expand(visibleChildren[0].element, false); + } + } + } catch (err) { + if (isPromiseCanceledError(err)) { + return; + } - this.setChildren(node, children, recursive); - }, err => { - if (isPromiseCanceledError(err)) { - return Promise.resolve(null); - } + slowTimeout.cancel(); + node.state = AsyncDataTreeNodeState.Uninitialized; + this._onDidChangeNodeState.fire(node); - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Uninitialized; - this._onDidChangeNodeState.fire(node); + if (node !== this.root) { + this.tree.collapse(node === this.root ? null : node); + } - if (node !== this.root) { - this.tree.collapse(node === this.root ? null : node); - } - - return Promise.reject(err); - }); + throw err; } } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 4390a4dc512..4c9fa0390a8 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -39,8 +39,9 @@ function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { } export interface IIndexTreeModelOptions { - collapseByDefault?: boolean; // defaults to false - filter?: ITreeFilter; + readonly collapseByDefault?: boolean; // defaults to false + readonly filter?: ITreeFilter; + readonly autoExpandSingleChildren?: boolean; } export class IndexTreeModel, TFilterData = void> implements ITreeModel { @@ -58,10 +59,12 @@ export class IndexTreeModel, TFilterData = voi private collapseByDefault: boolean; private filter?: ITreeFilter; + private autoExpandSingleChildren: boolean; constructor(private list: ISpliceable>, rootElement: T, options: IIndexTreeModelOptions = {}) { this.collapseByDefault = typeof options.collapseByDefault === 'undefined' ? false : options.collapseByDefault; this.filter = options.filter; + this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; // this.onDidChangeCollapseState(node => console.log(node.collapsed, node)); @@ -140,18 +143,45 @@ export class IndexTreeModel, TFilterData = voi } setCollapsed(location: number[], collapsed?: boolean, recursive?: boolean): boolean { - const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); + const node = this.getTreeNode(location); if (typeof collapsed === 'undefined') { collapsed = !node.collapsed; } - return this.eventBufferer.bufferEvents(() => { - return this._setCollapsed(node, listIndex, revealed, collapsed!, recursive || false); - }); + return this.eventBufferer.bufferEvents(() => this._setCollapsed(location, collapsed!, recursive)); } - private _setCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean { + private _setCollapsed(location: number[], collapsed: boolean, recursive?: boolean): boolean { + const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); + + const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false); + + if (this.autoExpandSingleChildren && !collapsed! && !recursive) { + let onlyVisibleChildIndex = -1; + + for (let i = 0; i < node.children.length; i++) { + const child = node.children[i]; + + if (child.visible) { + if (onlyVisibleChildIndex > -1) { + onlyVisibleChildIndex = -1; + break; + } else { + onlyVisibleChildIndex = i; + } + } + } + + if (onlyVisibleChildIndex > -1) { + this._setCollapsed([...location, onlyVisibleChildIndex], false, false); + } + } + + return result; + } + + private _setListNodeCollapsed(node: IMutableTreeNode, listIndex: number, revealed: boolean, collapsed: boolean, recursive: boolean): boolean { const result = this._setNodeCollapsed(node, collapsed, recursive, false); if (!revealed || !node.visible) { diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 7ed2449e061..3ac8b1b3353 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -10,7 +10,7 @@ import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { - sorter?: ITreeSorter; + readonly sorter?: ITreeSorter; } export class ObjectTreeModel, TFilterData extends NonNullable = void> implements ITreeModel { 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 850055107f1..9550ca9bdf6 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -251,7 +251,8 @@ export class ExplorerView extends ViewletPanel { multipleSelectionSupport: true, filter: this.filter, sorter: this.instantiationService.createInstance(FileSorter), - dnd: this.instantiationService.createInstance(FileDragAndDrop) + dnd: this.instantiationService.createInstance(FileDragAndDrop), + autoExpandSingleChildren: true }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); this.disposables.push(this.tree); From c85be2eb328909ddf6c7abe8fa2ffc8bee614681 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 15 Jan 2019 16:09:50 +0100 Subject: [PATCH 0256/1066] Fix #66542 --- .../extensionManagement/node/extensionGalleryService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index 77a3b6ce77f..ef888757750 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -353,7 +353,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { getExtension({ id, uuid }: IExtensionIdentifier, version?: string): Promise { let query = new Query() - .withFlags(Flags.IncludeAssetUri, Flags.IncludeStatistics, Flags.IncludeFiles, Flags.IncludeVersionProperties) + .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)); @@ -578,7 +578,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { getAllVersions(extension: IGalleryExtension, compatible: boolean): Promise { let query = new Query() - .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties) + .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) .withPage(1, 1) .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); @@ -609,7 +609,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { return Promise.resolve(extension); } const query = new Query() - .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties) + .withFlags(Flags.IncludeVersions, Flags.IncludeFiles, Flags.IncludeVersionProperties, Flags.ExcludeNonValidated) .withPage(1, 1) .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)) From 8faf4da02364816d5cf1301d437ff520b7b081a7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 16:35:25 +0100 Subject: [PATCH 0257/1066] explorer: bring back tree state preservation across reloads --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 109 ++++++++++++++---- .../electron-browser/views/explorerView.ts | 30 +++-- 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index a02ddccef81..1aa5dae1a95 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -234,11 +234,26 @@ export interface IAsyncDataTreeOptions extends IAbstractT autoExpandSingleChildren?: boolean; } +export interface IAsyncDataTreeViewState { + readonly focus: string[]; + readonly selection: string[]; + readonly expanded: string[]; +} + +interface IAsyncDataTreeViewStateContext { + readonly viewState: IAsyncDataTreeViewState; + readonly selection: IAsyncDataTreeNode[]; + readonly focus: IAsyncDataTreeNode[]; +} + export class AsyncDataTree implements IDisposable { private readonly tree: ObjectTree, TFilterData>; private readonly root: IAsyncDataTreeNode; 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; @@ -342,20 +357,28 @@ export class AsyncDataTree implements IDisposable return this.root.element as TInput; } - setInput(input: TInput): Promise { + async setInput(input: TInput, viewState?: IAsyncDataTreeViewState): Promise { this.refreshPromises.forEach(promise => promise.cancel()); this.refreshPromises.clear(); this.root.element = input!; - return this.refresh(input); + + const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext; + + await this.refresh(input, true, viewStateContext); + + if (viewStateContext) { + this.tree.setFocus(viewStateContext.focus); + this.tree.setSelection(viewStateContext.selection); + } } - refresh(element: TInput | T = this.root.element, recursive = true): Promise { + refresh(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { if (typeof this.root.element === 'undefined') { throw new Error('Tree input not set'); } - return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh); + return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } // Tree @@ -505,17 +528,15 @@ export class AsyncDataTree implements IDisposable return node; } - private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { - await this.queueRefresh(node, recursive, reason); + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + await this.queueRefresh(node, recursive, reason, viewStateContext); if (recursive && node.children) { - await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason))); + await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason, viewStateContext))); } } - private currentRefreshCalls = new Map, Promise>(); - - private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { + private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { if (node.state === AsyncDataTreeNodeState.Disposed) { console.error('Async data tree node is disposed'); return; @@ -525,7 +546,7 @@ export class AsyncDataTree implements IDisposable this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { if (intersects(refreshNode, node)) { - result = refreshPromise.then(() => this.queueRefresh(node, recursive, reason)); + result = refreshPromise.then(() => this.queueRefresh(node, recursive, reason, viewStateContext)); } }); @@ -533,17 +554,17 @@ export class AsyncDataTree implements IDisposable return result; } - result = this.doRefresh(node, recursive, reason); + result = this.doRefresh(node, recursive, reason, viewStateContext); this.currentRefreshCalls.set(node, result); return always(result, () => this.currentRefreshCalls.delete(node)); } - private async doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason): Promise { + private async doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { const hasChildren = !!this.dataSource.hasChildren(node.element!); if (!hasChildren) { - this.setChildren(node, [], recursive); + this.setChildren(node, [], recursive, viewStateContext); return; } @@ -568,7 +589,7 @@ export class AsyncDataTree implements IDisposable node.state = AsyncDataTreeNodeState.Loaded; this._onDidChangeNodeState.fire(node); - this.setChildren(node, children, recursive); + this.setChildren(node, children, recursive, viewStateContext); if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { const treeNode = this.tree.getNode(node); @@ -617,7 +638,7 @@ export class AsyncDataTree implements IDisposable } } - private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean): void { + private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): void { let nodeChildren: Map> | undefined; if (this.identityProvider) { @@ -645,16 +666,28 @@ export class AsyncDataTree implements IDisposable const asyncDataTreeNode = nodeChildren!.get(id); if (!asyncDataTreeNode) { + const childAsyncDataTreeNode: IAsyncDataTreeNode = { + element, + parent: node, + id, + children: [], + state: AsyncDataTreeNodeState.Uninitialized + }; + + if (viewStateContext) { + if (viewStateContext.viewState.focus.indexOf(id) > -1) { + viewStateContext.focus.push(childAsyncDataTreeNode); + } + + if (viewStateContext.viewState.selection.indexOf(id) > -1) { + viewStateContext.selection.push(childAsyncDataTreeNode); + } + } + return { - element: { - element, - parent: node, - id, - children: [], - state: AsyncDataTreeNodeState.Uninitialized - }, + element: childAsyncDataTreeNode, collapsible: !!this.dataSource.hasChildren(element), - collapsed: true + collapsed: typeof viewStateContext === 'undefined' ? true : viewStateContext.viewState.expanded.indexOf(id) === -1 }; } @@ -716,6 +749,34 @@ export class AsyncDataTree implements IDisposable } } + // view state + + getViewState(): IAsyncDataTreeViewState { + if (!this.identityProvider) { + throw new Error('Can\'t get tree view state without an identity provider'); + } + + const getId = (element: T) => this.identityProvider!.getId(element).toString(); + const focus = this.getFocus().map(getId); + const selection = this.getSelection().map(getId); + + const expanded: string[] = []; + const root = this.tree.getNode(); + const queue = [root]; + + while (queue.length > 0) { + const node = queue.shift()!; + + if (node !== root && !node.collapsed) { + expanded.push(getId(node.element!.element as T)); + } + + queue.push(...node.children); + } + + return { focus, selection, expanded }; + } + dispose(): void { dispose(this.disposables); } 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 9550ca9bdf6..390dc575290 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -43,9 +43,12 @@ import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { createFileIconThemableTreeContainerScope } from 'vs/workbench/browser/parts/views/views'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; + static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState'; private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; @@ -80,7 +83,8 @@ export class ExplorerView extends ViewletPanel { @IMenuService private readonly menuService: IMenuService, @IClipboardService private readonly clipboardService: IClipboardService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IExplorerService private readonly explorerService: IExplorerService + @IExplorerService private readonly explorerService: IExplorerService, + @IStorageService private readonly storageService: IStorageService ) { super({ ...(options as IViewletPanelOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService); @@ -189,7 +193,7 @@ export class ExplorerView extends ViewletPanel { if (activeFile) { this.explorerService.select(this.getActiveFile()); } else { - this.tree.setSelection([]); + // this.tree.setSelection([]); } } })); @@ -254,8 +258,8 @@ export class ExplorerView extends ViewletPanel { dnd: this.instantiationService.createInstance(FileDragAndDrop), autoExpandSingleChildren: true }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); - this.disposables.push(this.tree); + // Bind context keys FilesExplorerFocusedContext.bindTo(this.tree.contextKeyService); ExplorerFocusedContext.bindTo(this.tree.contextKeyService); @@ -311,6 +315,11 @@ export class ExplorerView extends ViewletPanel { })); this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); + + // save view state on shutdown + this.storageService.onWillSaveState(() => { + this.storageService.store(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, JSON.stringify(this.tree.getViewState()), StorageScope.WORKSPACE); + }, null, this.disposables); } // React on events @@ -384,6 +393,8 @@ export class ExplorerView extends ViewletPanel { return DOM.getLargestChildWidth(parentNode, childNodes); } + // private didLoad = false; + private setTreeInput(): Promise { if (!this.isBodyVisible()) { this.shouldRefresh = true; @@ -401,7 +412,14 @@ export class ExplorerView extends ViewletPanel { input = roots; } - const promise = this.tree.setInput(input).then(() => { + const rawViewState = this.storageService.get(ExplorerView.TREE_VIEW_STATE_STORAGE_KEY, StorageScope.WORKSPACE); + let viewState: IAsyncDataTreeViewState | undefined; + + if (rawViewState) { + viewState = JSON.parse(rawViewState) as IAsyncDataTreeViewState; + } + + const promise = this.tree.setInput(input, viewState).then(() => { // Find resource to focus from active editor input if set if (this.autoReveal && initialInputSetup) { const resourceToFocus = this.getActiveFile(); @@ -451,10 +469,6 @@ export class ExplorerView extends ViewletPanel { this.tree.reveal(fileStat, 0.5); } - if (!fileStat.isDirectory) { - this.tree.setSelection([fileStat]); // Since folders can not be opened, only select files - } - this.tree.setFocus([fileStat]); }); } From 6a8f824e4bc5aec177f25c6e932f0dabe588d98c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 16:41:42 +0100 Subject: [PATCH 0258/1066] select all command on trees fixes #63717 --- src/vs/workbench/electron-browser/commands.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index a9967f6ca44..6c97ce6a45e 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -25,6 +25,7 @@ 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 @@ -589,6 +590,28 @@ export function registerCommands(): void { const list = focused; list.setSelection(range(list.length)); } + + // Trees + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + const selection: any[] = []; + const visit = (node: ITreeNode) => { + node.children.forEach(child => { + if (child.visible) { + selection.push(child.element); + + if (!child.collapsed) { + visit(child); + } + } + }); + }; + + visit(tree.getNode()); + + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setSelection(selection, fakeKeyboardEvent); + } } }); From 43846b8cd658ad7f46949eb1908174330eba5d0b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 16:41:52 +0100 Subject: [PATCH 0259/1066] repl: :lipstick: --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 +- src/vs/workbench/parts/debug/electron-browser/repl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 1aa5dae1a95..71ef08ac021 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -383,7 +383,7 @@ export class AsyncDataTree implements IDisposable // Tree - getNode(element: TInput | T): ITreeNode { + getNode(element: TInput | T = this.root.element): ITreeNode { const dataNode = this.getDataNode(element); const node = this.tree.getNode(dataNode === this.root ? null : dataNode); return new AsyncDataTreeNodeWrapper(node); diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 47a2fab74b6..a2fbaafc01a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -258,7 +258,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati } }); }; - traverseAndAppend(this.tree.getNode(null)); + traverseAndAppend(this.tree.getNode()); return removeAnsiEscapeCodes(text); } From a97c668e5991e608c2c3fcfafd481d7c54a5a5e9 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 16:52:12 +0100 Subject: [PATCH 0260/1066] remove accidental comment out of line --- .../parts/files/electron-browser/views/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 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 390dc575290..099ff2428a6 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -193,7 +193,7 @@ export class ExplorerView extends ViewletPanel { if (activeFile) { this.explorerService.select(this.getActiveFile()); } else { - // this.tree.setSelection([]); + this.tree.setSelection([]); } } })); From 26cca958086975a4e5155053e0f0f5218c6dd0b8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 15 Jan 2019 16:53:54 +0100 Subject: [PATCH 0261/1066] async data tree: fix expansion of expanding node --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 71ef08ac021..33c0d3556ab 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -397,7 +397,7 @@ export class AsyncDataTree implements IDisposable async expand(element: T, recursive: boolean = false): Promise { const node = this.getDataNode(element); - if (node !== this.root && !this.tree.isCollapsed(node === this.root ? null : node)) { + if (node !== this.root && node.state !== AsyncDataTreeNodeState.Loading && !this.tree.isCollapsed(node)) { return false; } From f2e20240a4451824104f47e9afc69892450cfbea Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Jan 2019 07:51:05 -0800 Subject: [PATCH 0262/1066] fix #66478 --- src/vs/base/common/uri.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index bd39facb709..29524b9d7f0 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -108,7 +108,10 @@ export class URI implements UriComponents { && typeof (thing).fragment === 'string' && typeof (thing).path === 'string' && typeof (thing).query === 'string' - && typeof (thing).scheme === 'string'; + && typeof (thing).scheme === 'string' + && typeof (thing).fsPath === 'function' + && typeof (thing).with === 'function' + && typeof (thing).toString === 'function'; } /** From 1bae354b9be012ffbdb63dec7f390938fc7cf225 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 15 Jan 2019 16:59:15 +0100 Subject: [PATCH 0263/1066] explorer: select and reveal active file when explorer becomes visible fixes #66533 --- .../electron-browser/views/explorerView.ts | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 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 099ff2428a6..e6f47540ace 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -201,12 +201,19 @@ export class ExplorerView extends ViewletPanel { // Also handle configuration updates this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue(), e))); - this.disposables.push(this.onDidChangeBodyVisibility(visible => { + this.disposables.push(this.onDidChangeBodyVisibility(async visible => { if (visible) { // If a refresh was requested and we are now visible, run it if (this.shouldRefresh) { this.shouldRefresh = false; - this.setTreeInput().then(undefined, onUnexpectedError); + await this.setTreeInput(); + } + // Find resource to focus from active editor input if set + if (this.autoReveal) { + const activeFile = this.getActiveFile(); + if (activeFile) { + this.explorerService.select(activeFile, true); + } } } })); @@ -420,16 +427,6 @@ export class ExplorerView extends ViewletPanel { } const promise = this.tree.setInput(input, viewState).then(() => { - // Find resource to focus from active editor input if set - if (this.autoReveal && initialInputSetup) { - const resourceToFocus = this.getActiveFile(); - if (resourceToFocus) { - return this.explorerService.select(resourceToFocus, true); - } - } - - return undefined; - }).then(() => { if (initialInputSetup) { perf.mark('didResolveExplorer'); } From 4d22626a73e187917dbbea3c594a6b38725da8cf Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 15 Jan 2019 17:33:44 +0100 Subject: [PATCH 0264/1066] Fix #66450 --- src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts index bf526b22aeb..b70f618d51a 100644 --- a/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/browser/extensionsWidgets.ts @@ -119,7 +119,6 @@ export class RatingsWidget extends ExtensionWidget { const count = append(this.container, $('span.count')); count.textContent = String(rating); - this.container.title = this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('ratedBySingleUser', "Rated by 1 user"); } else { for (let i = 1; i <= 5; i++) { if (rating >= i) { @@ -131,5 +130,6 @@ export class RatingsWidget extends ExtensionWidget { } } } + this.container.title = this.extension.ratingCount > 1 ? localize('ratedByUsers', "Rated by {0} users", this.extension.ratingCount) : localize('ratedBySingleUser', "Rated by 1 user"); } } From ebf240126deb96282cb4bd4e92a0061292e12860 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 15 Jan 2019 17:46:08 +0100 Subject: [PATCH 0265/1066] storage - narrow types for store() method --- src/vs/base/node/storage.ts | 4 ++-- src/vs/platform/storage/common/storage.ts | 7 +++---- src/vs/platform/storage/node/storageService.ts | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index bd8b9b0bc6a..316b130217c 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -64,7 +64,7 @@ export interface IStorage extends IDisposable { getInteger(key: string, fallbackValue: number): number; getInteger(key: string, fallbackValue?: number): number | undefined; - set(key: string, value: any): Promise; + set(key: string, value: string | boolean | number): Promise; delete(key: string): Promise; close(): Promise; @@ -207,7 +207,7 @@ export class Storage extends Disposable implements IStorage { return parseInt(value, 10); } - set(key: string, value: any): Promise { + set(key: string, value: string | boolean | number): Promise { if (this.state === StorageState.Closed) { return Promise.resolve(); // Return early if we are already closed } diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 158996617e2..54d0aeb6fe0 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -71,13 +71,12 @@ export interface IStorageService { getInteger(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; /** - * Store a string value under the given key to storage. The value will - * be converted to a string. + * Store a value under the given key to storage. The value will be converted to a string. * * The scope argument allows to define the scope of the storage * operation to either the current workspace only or all workspaces. */ - store(key: string, value: any, scope: StorageScope): void; + store(key: string, value: string | boolean | number, scope: StorageScope): void; /** * Delete an element stored under the provided key from storage. @@ -154,7 +153,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic return parseInt(value, 10); } - store(key: string, value: any, scope: StorageScope): Promise { + store(key: string, value: string | boolean | number, scope: StorageScope): Promise { // We remove the key for undefined/null values if (isUndefinedOrNull(value)) { diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index b9af98020d2..feacaec0b54 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -205,7 +205,7 @@ export class StorageService extends Disposable implements IStorageService { return this.getStorage(scope).getInteger(key, fallbackValue); } - store(key: string, value: any, scope: StorageScope): void { + store(key: string, value: string | boolean | number, scope: StorageScope): void { this.getStorage(scope).set(key, value); } From dae8c42bea6f386fa3aafb4c1136b69df38b0618 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 15 Jan 2019 17:55:42 +0100 Subject: [PATCH 0266/1066] Fix #66364 --- .../workbench/parts/preferences/browser/keybindingWidgets.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts index c4bcaa98316..8fc40dc505d 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts @@ -86,10 +86,6 @@ export class KeybindingsSearchWidget extends SearchWidget { this.inputBox.value = this._inputValue; } - focus(): void { - this.inputBox.focus(); - } - private _reset() { this._firstPart = null; this._chordPart = null; From 96633087be2447c64fa2268d9a9b0894427ad616 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 14 Jan 2019 22:24:45 -0800 Subject: [PATCH 0267/1066] Strict null check explorer service --- src/tsconfig.strictNullChecks.json | 1 + .../parts/files/common/explorerModel.ts | 2 +- src/vs/workbench/parts/files/common/files.ts | 2 +- .../files/electron-browser/explorerService.ts | 43 ++++++++++--------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index ebed45968bd..53edad2e20b 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -630,6 +630,7 @@ "./vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts", "./vs/workbench/parts/files/common/explorerModel.ts", "./vs/workbench/parts/files/common/files.ts", + "./vs/workbench/parts/files/electron-browser/explorerService.ts", "./vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts", "./vs/workbench/parts/localizations/electron-browser/localizationsActions.ts", "./vs/workbench/parts/logs/common/logConstants.ts", diff --git a/src/vs/workbench/parts/files/common/explorerModel.ts b/src/vs/workbench/parts/files/common/explorerModel.ts index 085c63f8ef4..db6a66248c8 100644 --- a/src/vs/workbench/parts/files/common/explorerModel.ts +++ b/src/vs/workbench/parts/files/common/explorerModel.ts @@ -148,7 +148,7 @@ export class ExplorerItem { return this === this.root; } - static create(raw: IFileStat, parent: ExplorerItem, resolveTo?: URI[]): ExplorerItem { + static create(raw: IFileStat, parent: ExplorerItem | undefined, resolveTo?: URI[]): ExplorerItem { const stat = new ExplorerItem(raw.resource, parent, raw.isDirectory, raw.isSymbolicLink, raw.isReadonly, raw.name, raw.mtime, raw.etag); // Recursively add children if present diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index f7c25973e45..6472cf2147e 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -44,7 +44,7 @@ export interface IExplorerService { readonly onDidChangeRoots: Event; readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; - readonly onDidSelectItem: Event<{ item: ExplorerItem, reveal: boolean }>; + readonly onDidSelectItem: Event<{ item?: ExplorerItem, reveal?: boolean }>; setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 3e6c9da424c..0428d4d3539 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -18,8 +18,8 @@ import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/co import { IExpression } from 'vs/base/common/glob'; function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { - const scope = root ? { resource: root } : void 0; - const configuration = configurationService.getValue(scope); + const scope = root ? { resource: root } : undefined; + const configuration = scope ? configurationService.getValue(scope) : configurationService.getValue(); return (configuration && configuration.files && configuration.files.exclude) || Object.create(null); } @@ -30,9 +30,9 @@ export class ExplorerService implements IExplorerService { private static readonly EXPLORER_FILE_CHANGES_REACT_DELAY = 500; // delay in ms to react to file changes to give our internal events a chance to react first private _onDidChangeRoots = new Emitter(); - private _onDidChangeItem = new Emitter(); + private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); - private _onDidSelectItem = new Emitter<{ item: ExplorerItem, reveal: boolean }>(); + private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>(); private disposables: IDisposable[] = []; private editableStats = new Map(); private _sortOrder: SortOrder; @@ -52,7 +52,7 @@ export class ExplorerService implements IExplorerService { return this._onDidChangeRoots.event; } - get onDidChangeItem(): Event { + get onDidChangeItem(): Event { return this._onDidChangeItem.event; } @@ -60,7 +60,7 @@ export class ExplorerService implements IExplorerService { return this._onDidChangeEditable.event; } - get onDidSelectItem(): Event<{ item: ExplorerItem, reveal: boolean }> { + get onDidSelectItem(): Event<{ item?: ExplorerItem, reveal?: boolean }> { return this._onDidSelectItem.event; } @@ -72,7 +72,7 @@ export class ExplorerService implements IExplorerService { @memoize private get fileEventsFilter(): ResourceGlobMatcher { const fileEventsFilter = this.instantiationService.createInstance( ResourceGlobMatcher, - (root: URI) => getFileEventsExcludes(this.configurationService, root), + (root?: URI) => getFileEventsExcludes(this.configurationService, root), (event: IConfigurationChangeEvent) => event.affectsConfiguration(FILES_EXCLUDE_CONFIG) ); this.disposables.push(fileEventsFilter); @@ -86,7 +86,7 @@ export class ExplorerService implements IExplorerService { this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e))); this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e))); this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); - this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(null))); + this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(undefined))); this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire())); return model; @@ -94,7 +94,7 @@ export class ExplorerService implements IExplorerService { // IExplorerService methods - findClosest(resource: URI): ExplorerItem { + findClosest(resource: URI): ExplorerItem | null { return this.model.findClosest(resource); } @@ -111,23 +111,23 @@ export class ExplorerService implements IExplorerService { const fileStat = this.findClosest(resource); if (fileStat) { this._onDidSelectItem.fire({ item: fileStat, reveal }); - return Promise.resolve(null); + return Promise.resolve(undefined); } // Stat needs to be resolved first and then revealed const options: IResolveFileOptions = { resolveTo: [resource] }; const workspaceFolder = this.contextService.getWorkspaceFolder(resource); const rootUri = workspaceFolder ? workspaceFolder.uri : this.roots[0].resource; - const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop(); + const root = this.roots.filter(r => r.resource.toString() === rootUri.toString()).pop()!; return this.fileService.resolveFile(rootUri, options).then(stat => { // Convert to model - const modelStat = ExplorerItem.create(stat, null, options.resolveTo); + const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); // Select and Reveal - this._onDidSelectItem.fire({ item: root.find(resource), reveal }); + this._onDidSelectItem.fire({ item: root.find(resource) || undefined, reveal }); }, () => { root.isError = true; this._onDidChangeItem.fire(root); @@ -136,7 +136,7 @@ export class ExplorerService implements IExplorerService { refresh(): void { this.model.roots.forEach(r => r.isDirectoryResolved = false); - this._onDidChangeItem.fire(null); + this._onDidChangeItem.fire(undefined); } // File events @@ -144,8 +144,8 @@ export class ExplorerService implements IExplorerService { private onFileOperation(e: FileOperationEvent): void { // Add if (e.operation === FileOperation.CREATE || e.operation === FileOperation.COPY) { - const addedElement = e.target; - const parentResource = dirname(addedElement.resource); + const addedElement = e.target!; + const parentResource = dirname(addedElement.resource)!; const parents = this.model.findAll(parentResource); if (parents.length) { @@ -153,7 +153,7 @@ export class ExplorerService implements IExplorerService { // Add the new file to its parent (Model) parents.forEach(p => { // We have to check if the parent is resolved #29177 - const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource); + const thenable: Promise = p.isDirectoryResolved ? Promise.resolve(undefined) : this.fileService.resolveFile(p.resource); thenable.then(stat => { if (stat) { const modelStat = ExplorerItem.create(stat, p.parent); @@ -174,7 +174,7 @@ export class ExplorerService implements IExplorerService { // Move (including Rename) else if (e.operation === FileOperation.MOVE) { const oldResource = e.resource; - const newElement = e.target; + const newElement = e.target!; const oldParentResource = dirname(oldResource); const newParentResource = dirname(newElement.resource); @@ -243,6 +243,9 @@ export class ExplorerService implements IExplorerService { // Find parent const parent = dirname(change.resource); + if (!parent) { + continue; + } // Continue if parent was already determined as to be ignored if (ignoredPaths[parent.toString()]) { @@ -270,7 +273,7 @@ export class ExplorerService implements IExplorerService { for (let j = 0; j < deleted.length; j++) { const del = deleted[j]; const item = this.model.findClosest(del.resource); - if (item) { + if (item && item.parent) { explorerItemChanged(item.parent); } } @@ -285,7 +288,7 @@ export class ExplorerService implements IExplorerService { const upd = updated[j]; const item = this.model.findClosest(upd.resource); - if (item) { + if (item && item.parent) { explorerItemChanged(item.parent); } } From 9634301153c6a5dbf70b5473ac0e432f27fbfce3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 15 Jan 2019 10:46:27 -0800 Subject: [PATCH 0268/1066] Strict null check localizations --- src/tsconfig.strictNullChecks.json | 1 + .../localizations.contribution.ts | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 53edad2e20b..94d453228f4 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -632,6 +632,7 @@ "./vs/workbench/parts/files/common/files.ts", "./vs/workbench/parts/files/electron-browser/explorerService.ts", "./vs/workbench/parts/files/electron-browser/views/explorerDecorationsProvider.ts", + "./vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts", "./vs/workbench/parts/localizations/electron-browser/localizationsActions.ts", "./vs/workbench/parts/logs/common/logConstants.ts", "./vs/workbench/parts/logs/electron-browser/logs.contribution.ts", diff --git a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts index 6856801b960..f1d45ce3caa 100644 --- a/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts +++ b/src/vs/workbench/parts/localizations/electron-browser/localizations.contribution.ts @@ -83,7 +83,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"), run: () => { const file = URI.file(join(this.environmentService.appSettingsHome, 'locale.json')); - const updatePromise = updateAndRestart ? this.jsonEditingService.write(file, { key: 'locale', value: locale }, true) : Promise.resolve(null); + const updatePromise = updateAndRestart ? this.jsonEditingService.write(file, { key: 'locale', value: locale }, true) : Promise.resolve(undefined); updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); } }, { @@ -105,7 +105,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo if (!this.galleryService.isEnabled()) { return; } - if (language === 'en' || language.indexOf('en-') === 0) { + if (!language || !locale || language === 'en' || language.indexOf('en-') === 0) { return; } if (language === locale || languagePackSuggestionIgnoreList.indexOf(language) > -1) { @@ -211,9 +211,14 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo } - private isLanguageInstalled(language: string): Promise { + private isLanguageInstalled(language: string | undefined): Promise { return this.extensionManagementService.getInstalled(ExtensionType.User) - .then(installed => installed.some(i => i.manifest && i.manifest.contributes && i.manifest.contributes.localizations && i.manifest.contributes.localizations.length && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language))); + .then(installed => installed.some(i => + !!(i.manifest + && i.manifest.contributes + && i.manifest.contributes.localizations + && i.manifest.contributes.localizations.length + && i.manifest.contributes.localizations.some(l => l.languageId.toLowerCase() === language)))); } private installExtension(extension: IGalleryExtension): Promise { @@ -221,7 +226,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo .then(viewlet => viewlet as IExtensionsViewlet) .then(viewlet => viewlet.search(`@id:${extension.identifier.id}`)) .then(() => this.extensionManagementService.installFromGallery(extension)) - .then(() => null, err => this.notificationService.error(err)); + .then(() => undefined, err => this.notificationService.error(err)); } } @@ -248,7 +253,7 @@ function registerLocaleDefinitionSchema(languages: string[]): void { }); } -registerLocaleDefinitionSchema([platform.language]); +registerLocaleDefinitionSchema(platform.language ? [platform.language] : []); const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(LocalizationWorkbenchContribution, LifecyclePhase.Eventually); From 1a70a0a2d136284c8447ee3238ae8571e837d511 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 15 Jan 2019 10:53:08 -0800 Subject: [PATCH 0269/1066] Working on strict null checks for snippets --- .../editor/contrib/suggest/suggestController.ts | 16 +++++++++++----- .../snippets/electron-browser/insertSnippet.ts | 15 ++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 9e899b3aa91..609e8340208 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { alert } from 'vs/base/browser/ui/aria/aria'; -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -58,8 +58,8 @@ class AcceptOnCharacterOracle { })); } - private _onItem(selected: ISelectedSuggestion): void { - if (!selected || isFalsyOrEmpty(selected.item.completion.commitCharacters)) { + private _onItem(selected: ISelectedSuggestion | undefined): void { + if (!selected || !isNonEmptyArray(selected.item.completion.commitCharacters)) { this.reset(); return; } @@ -209,12 +209,15 @@ export class SuggestController implements IEditorContribution { } } - protected _onDidSelectItem(event: ISelectedSuggestion, keepAlternativeSuggestions: boolean, undoStops: boolean): void { + protected _onDidSelectItem(event: ISelectedSuggestion | undefined, keepAlternativeSuggestions: boolean, undoStops: boolean): void { if (!event || !event.item) { this._alternatives.getValue().reset(); this._model.cancel(); return; } + if (!this._editor.hasModel()) { + return; + } const model = this._editor.getModel(); const modelVersionNow = model.getAlternativeVersionId(); @@ -299,7 +302,10 @@ export class SuggestController implements IEditorContribution { } triggerSuggestAndAcceptBest(defaultTypeText: string): void { + if (!this._editor.hasModel()) { + return; + } const positionNow = this._editor.getPosition(); const fallback = () => { @@ -366,7 +372,7 @@ export class SuggestController implements IEditorContribution { acceptSelectedSuggestion(keepAlternativeSuggestions?: boolean): void { if (this._widget) { const item = this._widget.getValue().getFocusedItem(); - this._onDidSelectItem(item, keepAlternativeSuggestions, true); + this._onDidSelectItem(item, !!keepAlternativeSuggestions, true); } } diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 015514b1913..5acec09d7b4 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -41,13 +41,10 @@ class Args { private static readonly _empty = new Args(undefined, undefined, undefined); private constructor( - public readonly snippet: string, - public readonly name: string, - public readonly langId: string - ) { - - } - + public readonly snippet: string | undefined, + public readonly name: string | undefined, + public readonly langId: string | undefined + ) { } } class InsertSnippetAction extends EditorAction { @@ -61,7 +58,7 @@ class InsertSnippetAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise { + public run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise | undefined { const modeService = accessor.get(IModeService); const snippetService = accessor.get(ISnippetsService); @@ -116,7 +113,7 @@ class InsertSnippetAction extends EditorAction { // let user pick a snippet const snippets = (await snippetService.getSnippets(languageId)).sort(Snippet.compare); const picks: QuickPickInput[] = []; - let prevSnippet: Snippet; + let prevSnippet: Snippet | undefined; for (const snippet of snippets) { const pick: ISnippetPick = { label: snippet.prefix, From f27183be673087b968afc8c845fe4a1e0f66b4ec Mon Sep 17 00:00:00 2001 From: rebornix Date: Tue, 15 Jan 2019 11:56:48 -0800 Subject: [PATCH 0270/1066] add error code for git blame --- extensions/git/src/api/git.d.ts | 3 ++- extensions/git/src/git.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 5943ebe4b96..042add4adac 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -216,5 +216,6 @@ export const enum GitErrorCodes { WrongCase = 'WrongCase', CantLockRef = 'CantLockRef', CantRebaseMultipleBranches = 'CantRebaseMultipleBranches', - PatchDoesNotApply = 'PatchDoesNotApply' + PatchDoesNotApply = 'PatchDoesNotApply', + NoPathFound = 'NoPathFound' } \ No newline at end of file diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 4a7cf7b8fe8..e4b29e198ab 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1291,6 +1291,10 @@ export class Repository { return result.stdout.trim(); } catch (err) { + if (/^fatal: no such path/.test(err.stderr || '')) { + err.gitErrorCode = GitErrorCodes.NoPathFound; + } + throw err; } } From 63a73b04608d18aced79d2856b29317c0661c52b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Jan 2019 13:27:55 -0800 Subject: [PATCH 0271/1066] strict null - insert snippet, #66558 --- src/tsconfig.strictNullChecks.json | 3 ++- .../electron-browser/insertSnippet.ts | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 94d453228f4..ce8697582f7 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -671,6 +671,7 @@ "./vs/workbench/parts/search/test/common/searchModel.test.ts", "./vs/workbench/parts/search/test/common/searchResult.test.ts", "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", + "./vs/workbench/parts/snippets/electron-browser/insertSnippet.ts", "./vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts", "./vs/workbench/parts/snippets/electron-browser/snippets.contribution.ts", "./vs/workbench/parts/snippets/electron-browser/snippetsFile.ts", @@ -884,4 +885,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts index 5acec09d7b4..26e465de1fa 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/insertSnippet.ts @@ -58,7 +58,7 @@ class InsertSnippetAction extends EditorAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise | undefined { + run(accessor: ServicesAccessor, editor: ICodeEditor, arg: any): Promise | undefined { const modeService = accessor.get(IModeService); const snippetService = accessor.get(ISnippetsService); @@ -74,19 +74,22 @@ class InsertSnippetAction extends EditorAction { if (snippet) { return resolve(new Snippet( - undefined, - undefined, - undefined, - undefined, + [], + '', + '', + '', snippet, - undefined, + '', SnippetSource.User, )); } - let languageId: LanguageId; + let languageId = LanguageId.Null; if (langId) { - languageId = modeService.getLanguageIdentifier(langId).id; + const otherLangId = modeService.getLanguageIdentifier(langId); + if (otherLangId) { + languageId = otherLangId.id; + } } else { editor.getModel().tokenizeIfCheap(lineNumber); languageId = editor.getModel().getLanguageIdAtPosition(lineNumber, column); @@ -94,8 +97,8 @@ class InsertSnippetAction extends EditorAction { // validate the `languageId` to ensure this is a user // facing language with a name and the chance to have // snippets, else fall back to the outer language - const { language } = modeService.getLanguageIdentifier(languageId); - if (!modeService.getLanguageName(language)) { + const otherLangId = modeService.getLanguageIdentifier(languageId); + if (otherLangId && !modeService.getLanguageName(otherLangId.language)) { languageId = editor.getModel().getLanguageIdentifier().id; } } From 924b7a1fea137cdbc4bc2d821263260f66c4cf7e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 19:47:46 +0100 Subject: [PATCH 0272/1066] Handle the case of removing an extension from the extension host --- src/vs/workbench/api/node/extHost.protocol.ts | 1 + src/vs/workbench/api/node/extHostExtensionService.ts | 5 +++++ .../electron-browser/extensionHostProcessManager.ts | 4 ++++ .../extensions/electron-browser/extensionService.ts | 7 +++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index a408ee583e7..08baa733711 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -742,6 +742,7 @@ export interface ExtHostExtensionServiceShape { $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise; $addExtension(extension: IExtensionDescription): Promise; + $removeExtension(extension: ExtensionIdentifier): Promise; $test_latency(n: number): Promise; $test_up(b: Buffer): Promise; diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index b9b2916c111..fe143a9989f 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -675,6 +675,11 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(undefined); } + public $removeExtension(extensionId: ExtensionIdentifier): Promise { + this._registry.remove(extensionId); + return Promise.resolve(undefined); + } + public async $test_latency(n: number): Promise { return n; } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index e55bd38105d..f108a8b8e57 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -252,6 +252,10 @@ export class ExtensionHostProcessManager extends Disposable { public addExtension(extension: IExtensionDescription): Promise { return this._extensionHostProcessProxy.then(proxy => proxy.value.$addExtension(extension)); } + + public removeExtension(extensionId: ExtensionIdentifier): Promise { + return this._extensionHostProcessProxy.then(proxy => proxy.value.$removeExtension(extensionId)); + } } const colorTables = [ diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 0d047f46657..1cf2e65d437 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -142,7 +142,7 @@ export class ExtensionService extends Disposable implements IExtensionService { } } - private _removeExtension(extensionId: string): void { + private async _removeExtension(extensionId: string): Promise { const extensionDescription = this._registry.getExtensionDescription(extensionId); if (!extensionDescription) { // ignore disabling an extension which is not running @@ -156,9 +156,12 @@ export class ExtensionService extends Disposable implements IExtensionService { // Remove the extension from the local registry and from the extension host this._registry.remove(extensionDescription.identifier); - // TODO@Alex: remove from the extension host this._rehandleExtensionPoints(extensionDescription); + + if (this._extensionHostProcessManagers.length > 0) { + await this._extensionHostProcessManagers[0].removeExtension(extensionDescription.identifier); + } } private _rehandleExtensionPoints(extensionDescription: IExtensionDescription): void { From a73ca964867891cd84aeb7a33c5c35d82c04e64d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 19:56:58 +0100 Subject: [PATCH 0273/1066] Adopt more async/await --- .../textMate/electron-browser/TMSyntax.ts | 90 ++++++++++--------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 3b946bdc74d..98677231b6c 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -229,39 +229,42 @@ export class TextMateService extends Disposable implements ITextMateService { } } + private async _createGrammarRegistry(): Promise<[Registry, StackElement]> { + const { Registry, INITIAL, parseRawGrammar } = await import('vscode-textmate'); + const grammarRegistry = new Registry({ + loadGrammar: async (scopeName: string) => { + const location = this._scopeRegistry.getGrammarLocation(scopeName); + if (!location) { + this._logService.trace(`No grammar found for scope ${scopeName}`); + return null; + } + try { + const content = await this._fileService.resolveContent(location, { encoding: 'utf8' }); + return parseRawGrammar(content.value, location.path); + } catch (e) { + this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); + return null; + } + }, + getInjections: (scopeName: string) => { + const scopeParts = scopeName.split('.'); + let injections: string[] = []; + for (let i = 1; i <= scopeParts.length; i++) { + const subScopeName = scopeParts.slice(0, i).join('.'); + injections = [...injections, ...this._injections[subScopeName]]; + } + return injections; + } + }); + this._updateTheme(grammarRegistry); + this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); + return <[Registry, StackElement]>[grammarRegistry, INITIAL]; + } + private _getOrCreateGrammarRegistry(): Promise<[Registry, StackElement]> { if (!this._grammarRegistry) { - this._grammarRegistry = import('vscode-textmate').then(({ Registry, INITIAL, parseRawGrammar }) => { - const grammarRegistry = new Registry({ - loadGrammar: (scopeName: string) => { - const location = this._scopeRegistry.getGrammarLocation(scopeName); - if (!location) { - this._logService.trace(`No grammar found for scope ${scopeName}`); - return null; - } - return this._fileService.resolveContent(location, { encoding: 'utf8' }).then(content => { - return parseRawGrammar(content.value, location.path); - }, e => { - this._logService.error(`Unable to load and parse grammar for scope ${scopeName} from ${location}`, e); - return null; - }); - }, - getInjections: (scopeName: string) => { - const scopeParts = scopeName.split('.'); - let injections: string[] = []; - for (let i = 1; i <= scopeParts.length; i++) { - const subScopeName = scopeParts.slice(0, i).join('.'); - injections = [...injections, ...this._injections[subScopeName]]; - } - return injections; - } - }); - this._updateTheme(grammarRegistry); - this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); - return <[Registry, StackElement]>[grammarRegistry, INITIAL]; - }); + this._grammarRegistry = this._createGrammarRegistry(); } - return this._grammarRegistry; } @@ -384,11 +387,12 @@ export class TextMateService extends Disposable implements ITextMateService { return result; } - public createGrammar(modeId: string): Promise { - return this._createGrammar(modeId).then(r => r.grammar); + public async createGrammar(modeId: string): Promise { + const { grammar } = await this._createGrammar(modeId); + return grammar; } - private _createGrammar(modeId: string): Promise { + private async _createGrammar(modeId: string): Promise { const scopeName = this._languageToScope.get(modeId); if (typeof scopeName !== 'string') { // No TM grammar defined @@ -412,17 +416,15 @@ export class TextMateService extends Disposable implements ITextMateService { let languageId = this._modeService.getLanguageIdentifier(modeId)!.id; let containsEmbeddedLanguages = (Object.keys(embeddedLanguages).length > 0); - return this._getOrCreateGrammarRegistry().then((_res) => { - const [grammarRegistry, initialState] = _res; - return grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: languageRegistration.tokenTypes }).then(grammar => { - return { - languageId: languageId, - grammar: grammar, - initialState: initialState, - containsEmbeddedLanguages: containsEmbeddedLanguages - }; - }); - }); + + const [grammarRegistry, initialState] = await this._getOrCreateGrammarRegistry(); + const grammar = await grammarRegistry.loadGrammarWithConfiguration(scopeName, languageId, { embeddedLanguages, tokenTypes: languageRegistration.tokenTypes }); + return { + languageId: languageId, + grammar: grammar, + initialState: initialState, + containsEmbeddedLanguages: containsEmbeddedLanguages + }; } } From 67a36f705258a33556a015491ab52bf7282ff1f5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 20:42:51 +0100 Subject: [PATCH 0274/1066] Better clean-up of state when the extension point handler is called --- .../services/textMate/electron-browser/TMSyntax.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 98677231b6c..9bc2120abbf 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -150,8 +150,8 @@ export class TextMateService extends Disposable implements ITextMateService { private _languageToScope: Map; private _grammarRegistry: Promise<[Registry, StackElement]> | null; private _tokenizersRegistrations: IDisposable[]; - private _currentTokenColors: ITokenColorizationRule[] | null; + private _themeListener: IDisposable; constructor( @IModeService private readonly _modeService: IModeService, @@ -172,6 +172,7 @@ export class TextMateService extends Disposable implements ITextMateService { this._grammarRegistry = null; this._tokenizersRegistrations = []; this._currentTokenColors = null; + this._themeListener = null; grammarsExtPoint.setHandler((extensions) => { this._scopeRegistry.reset(); @@ -180,6 +181,11 @@ export class TextMateService extends Disposable implements ITextMateService { this._languageToScope = new Map(); this._grammarRegistry = null; this._tokenizersRegistrations = dispose(this._tokenizersRegistrations); + this._currentTokenColors = null; + if (this._themeListener) { + this._themeListener.dispose(); + this._themeListener = null; + } for (const extension of extensions) { let grammars = extension.value; @@ -257,7 +263,7 @@ export class TextMateService extends Disposable implements ITextMateService { } }); this._updateTheme(grammarRegistry); - this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); + this._themeListener = this._themeService.onDidColorThemeChange((e) => this._updateTheme(grammarRegistry)); return <[Registry, StackElement]>[grammarRegistry, INITIAL]; } From 7304f38e7ff626f4e941b3420ed5422b7517e3da Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 20:45:50 +0100 Subject: [PATCH 0275/1066] Activate on workspaceContains, enable dynamic extension points --- .../electron-browser/extensionService.ts | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 1cf2e65d437..a3fb979f728 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -34,7 +34,6 @@ import { Schemas } from 'vs/base/common/network'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); -const DYNAMIC_EXTENSION_POINTS = false; schema.properties.engines.properties.vscode.default = `^${pkg.version}`; @@ -115,31 +114,29 @@ export class ExtensionService extends Disposable implements IExtensionService { }]); } - if (DYNAMIC_EXTENSION_POINTS) { - this._extensionEnablementService.onEnablementChanged((extension) => { - if (this._extensionEnablementService.isEnabled(extension)) { - // an extension has been enabled - this._addExtension(extension); - } else { - // an extension has been disabled - this._removeExtension(extension.identifier.id); - } - }); + this._extensionEnablementService.onEnablementChanged((extension) => { + if (this._extensionEnablementService.isEnabled(extension)) { + // an extension has been enabled + this._addExtension(extension); + } else { + // an extension has been disabled + this._removeExtension(extension.identifier.id); + } + }); - this._extensionManagementService.onDidInstallExtension((event) => { - if (event.local) { - // an extension has been installed - this._addExtension(event.local); - } - }); + this._extensionManagementService.onDidInstallExtension((event) => { + if (event.local) { + // an extension has been installed + this._addExtension(event.local); + } + }); - this._extensionManagementService.onDidUninstallExtension((event) => { - if (!event.error) { - // an extension has been uninstalled - this._removeExtension(event.identifier.id); - } - }); - } + this._extensionManagementService.onDidUninstallExtension((event) => { + if (!event.error) { + // an extension has been uninstalled + this._removeExtension(event.identifier.id); + } + }); } private async _removeExtension(extensionId: string): Promise { @@ -255,10 +252,16 @@ export class ExtensionService extends Disposable implements IExtensionService { shouldActivateReason = activationEvent; break; } + + if (/^workspaceContains/.test(activationEvent)) { + // do not trigger a search, just activate in this case... + shouldActivate = true; + shouldActivateReason = activationEvent; + break; + } } } - // TODO@Alex: workspaceContains will not work if (shouldActivate) { await Promise.all( this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason)) From 124b80a1ec701c4a3ef73e82c2391de52ae999e7 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 15 Jan 2019 23:27:44 +0100 Subject: [PATCH 0276/1066] Fix strict null check --- src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts index 9bc2120abbf..3142782ef07 100644 --- a/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts +++ b/src/vs/workbench/services/textMate/electron-browser/TMSyntax.ts @@ -151,7 +151,7 @@ export class TextMateService extends Disposable implements ITextMateService { private _grammarRegistry: Promise<[Registry, StackElement]> | null; private _tokenizersRegistrations: IDisposable[]; private _currentTokenColors: ITokenColorizationRule[] | null; - private _themeListener: IDisposable; + private _themeListener: IDisposable | null; constructor( @IModeService private readonly _modeService: IModeService, From eecb58f091566584cb30c40e7b2b0bf679cf938d Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 15 Jan 2019 14:57:05 -0800 Subject: [PATCH 0277/1066] Properly dispose of button stylers on comments --- .../comments/electron-browser/commentNode.ts | 4 ++-- .../electron-browser/commentThreadWidget.ts | 16 ++++++++-------- 2 files changed, 10 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 fb99db2009f..21077bd08aa 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentNode.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentNode.ts @@ -227,7 +227,7 @@ export class CommentNode extends Disposable { const cancelEditButton = new Button(formActions); cancelEditButton.label = nls.localize('label.cancel', "Cancel"); - attachButtonStyler(cancelEditButton, this.themeService); + this._toDispose.push(attachButtonStyler(cancelEditButton, this.themeService)); this._toDispose.push(cancelEditButton.onDidClick(_ => { this.removeCommentEditor(); @@ -235,7 +235,7 @@ export class CommentNode extends Disposable { this._updateCommentButton = new Button(formActions); this._updateCommentButton.label = UPDATE_COMMENT_LABEL; - attachButtonStyler(this._updateCommentButton, this.themeService); + this._toDispose.push(attachButtonStyler(this._updateCommentButton, this.themeService)); this._toDispose.push(this._updateCommentButton.onDidClick(_ => { this.editComment(); diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index b4462a46bd9..b009344ee58 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -388,7 +388,7 @@ export class ReviewZoneWidget extends ZoneWidget { private createCommentWidgetActions(container: HTMLElement, model: ITextModel) { const button = new Button(container); - attachButtonStyler(button, this.themeService); + this._disposables.push(attachButtonStyler(button, this.themeService)); button.label = 'Add comment'; button.enabled = model.getValueLength() > 0; @@ -414,23 +414,23 @@ export class ReviewZoneWidget extends ZoneWidget { const deleteDraftLabel = this.commentService.getDeleteDraftLabel(this._owner); if (deleteDraftLabel) { const deletedraftButton = new Button(container); - attachButtonStyler(deletedraftButton, this.themeService); + this._disposables.push(attachButtonStyler(deletedraftButton, this.themeService)); deletedraftButton.label = deleteDraftLabel; deletedraftButton.enabled = true; - deletedraftButton.onDidClick(async () => { + this._disposables.push(deletedraftButton.onDidClick(async () => { try { await this.commentService.deleteDraft(this._owner, this.editor.getModel().uri); } catch (e) { this.handleError(e); } - }); + })); } const submitDraftLabel = this.commentService.getFinishDraftLabel(this._owner); if (submitDraftLabel) { const submitdraftButton = new Button(container); - attachButtonStyler(submitdraftButton, this.themeService); + this._disposables.push(attachButtonStyler(submitdraftButton, this.themeService)); submitdraftButton.label = this.commentService.getFinishDraftLabel(this._owner); submitdraftButton.enabled = true; @@ -452,7 +452,7 @@ export class ReviewZoneWidget extends ZoneWidget { const startDraftLabel = this.commentService.getStartDraftLabel(this._owner); if (startDraftLabel) { const draftButton = new Button(container); - attachButtonStyler(draftButton, this.themeService); + this._disposables.push(attachButtonStyler(draftButton, this.themeService)); draftButton.label = this.commentService.getStartDraftLabel(this._owner); draftButton.enabled = model.getValueLength() > 0; @@ -464,7 +464,7 @@ export class ReviewZoneWidget extends ZoneWidget { } })); - draftButton.onDidClick(async () => { + this._disposables.push(draftButton.onDidClick(async () => { try { await this.commentService.startDraft(this._owner, this.editor.getModel().uri); let lineNumber = this._commentGlyph.getPosition().position.lineNumber; @@ -472,7 +472,7 @@ export class ReviewZoneWidget extends ZoneWidget { } catch (e) { this.handleError(e); } - }); + })); } break; From 4b52f61161223909207319eee33be8533a4c1389 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Jan 2019 15:30:28 -0800 Subject: [PATCH 0278/1066] :lipstick: --- .../markers/electron-browser/markersFileDecorations.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts index 79586cb4ce4..ce1755e1f09 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersFileDecorations.ts @@ -92,8 +92,6 @@ class MarkersFileDecorations implements IWorkbenchContribution { } } -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(MarkersFileDecorations, LifecyclePhase.Restored); - Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ 'id': 'problems', 'order': 101, @@ -106,3 +104,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis } } }); + +// register file decorations +Registry.as(WorkbenchExtensions.Workbench) + .registerWorkbenchContribution(MarkersFileDecorations, LifecyclePhase.Restored); From cf2179fd9096ffdf97f16d9240d0914e330cc54e Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Tue, 15 Jan 2019 16:22:07 -0800 Subject: [PATCH 0279/1066] Fixes #66275, Process explorer shows 'node' processes as 'electron_node' --- src/vs/base/node/ps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/node/ps.ts b/src/vs/base/node/ps.ts index 8117be30736..e42e9420809 100644 --- a/src/vs/base/node/ps.ts +++ b/src/vs/base/node/ps.ts @@ -109,7 +109,7 @@ export function listProcesses(rootPid: number): Promise { } while (matches); if (result) { - if (cmd.indexOf('node ') !== 0) { + if (cmd.indexOf('node ') < 0 && cmd.indexOf('node.exe') < 0) { return `electron_node ${result}`; } } From 30b1f126da0d49b0c9900276bce32078b2ca3708 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Jan 2019 17:03:25 -0800 Subject: [PATCH 0280/1066] strict null suggest controller, fixes #66558 --- src/tsconfig.strictNullChecks.json | 14 ++------ .../contrib/suggest/suggestController.ts | 32 ++++++++++--------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index ce8697582f7..f97a1fbcac7 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -15,7 +15,8 @@ "./vs/editor/contrib/smartSelect/**/*.ts", "./vs/editor/contrib/format/**/*.ts", "./vs/editor/contrib/gotoError/**/*.ts", - "./vs/editor/contrib/inPlaceReplace/**/*.ts" + "./vs/editor/contrib/inPlaceReplace/**/*.ts", + "./vs/editor/contrib/suggest/**/*.ts" ], "files": [ "./vs/base/parts/contextmenu/common/contextmenu.ts", @@ -250,17 +251,6 @@ "./vs/editor/contrib/snippet/test/snippetController2.test.ts", "./vs/editor/contrib/snippet/test/snippetParser.test.ts", "./vs/editor/contrib/snippet/test/snippetVariables.test.ts", - "./vs/editor/contrib/suggest/completionModel.ts", - "./vs/editor/contrib/suggest/suggest.ts", - "./vs/editor/contrib/suggest/suggestAlternatives.ts", - "./vs/editor/contrib/suggest/suggestMemory.ts", - "./vs/editor/contrib/suggest/suggestModel.ts", - "./vs/editor/contrib/suggest/suggestWidget.ts", - "./vs/editor/contrib/suggest/test/completionModel.test.ts", - "./vs/editor/contrib/suggest/test/suggest.test.ts", - "./vs/editor/contrib/suggest/test/suggestMemory.test.ts", - "./vs/editor/contrib/suggest/wordContextKey.ts", - "./vs/editor/contrib/suggest/wordDistance.ts", "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", "./vs/editor/contrib/wordOperations/test/wordOperations.test.ts", diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 609e8340208..e0d9982667f 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -129,7 +129,7 @@ export class SuggestController implements IEditorContribution { let makesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService); this._toDispose.push(widget.onDidFocus(({ item }) => { - const position = this._editor.getPosition(); + const position = this._editor.getPosition()!; const startColumn = item.completion.range.startColumn; const endColumn = position.column; let value = true; @@ -138,10 +138,10 @@ export class SuggestController implements IEditorContribution { && this._model.state === State.Auto && !item.completion.command && !item.completion.additionalTextEdits - && !(item.completion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet) + && !(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet) && endColumn - startColumn === item.completion.insertText.length ) { - const oldText = this._editor.getModel().getValueInRange({ + const oldText = this._editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -171,7 +171,7 @@ export class SuggestController implements IEditorContribution { })); this._toDispose.push(this._model.onDidSuggest(e => { if (!e.shy) { - let index = this._memoryService.select(this._editor.getModel(), this._editor.getPosition(), e.completionModel.items); + let index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, e.completionModel.items); this._widget.getValue().showSuggestions(e.completionModel, index, e.isFrozen, e.auto); } })); @@ -239,7 +239,7 @@ export class SuggestController implements IEditorContribution { this._memoryService.memorize(model, this._editor.getPosition(), event.item); let { insertText } = suggestion; - if (!(suggestion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)) { + if (!(suggestion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) { insertText = SnippetParser.escape(insertText); } @@ -251,7 +251,7 @@ export class SuggestController implements IEditorContribution { overwriteBefore + columnDelta, overwriteAfter, false, false, - !(suggestion.insertTextRules & CompletionItemInsertTextRule.KeepWhitespace) + !(suggestion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace) ); if (undoStops) { @@ -268,7 +268,7 @@ export class SuggestController implements IEditorContribution { } else { // exec command, done - this._commandService.executeCommand(suggestion.command.id, ...suggestion.command.arguments).catch(onUnexpectedError); + this._commandService.executeCommand(suggestion.command.id, suggestion.command.arguments ? [...suggestion.command.arguments] : []).catch(onUnexpectedError); this._model.cancel(); } @@ -296,9 +296,11 @@ export class SuggestController implements IEditorContribution { } triggerSuggest(onlyFrom?: CompletionItemProvider[]): void { - this._model.trigger({ auto: false, shy: false }, false, onlyFrom); - this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); - this._editor.focus(); + if (this._editor.hasModel()) { + this._model.trigger({ auto: false, shy: false }, false, onlyFrom); + this._editor.revealLine(this._editor.getPosition().lineNumber, ScrollType.Smooth); + this._editor.focus(); + } } triggerSuggestAndAcceptBest(defaultTypeText: string): void { @@ -309,24 +311,24 @@ export class SuggestController implements IEditorContribution { const positionNow = this._editor.getPosition(); const fallback = () => { - if (positionNow.equals(this._editor.getPosition())) { + if (positionNow.equals(this._editor.getPosition()!)) { this._editor.trigger('suggest', Handler.Type, { text: defaultTypeText }); } }; const makesTextEdit = (item: CompletionItem): boolean => { - if (item.completion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet || item.completion.additionalTextEdits) { + if (item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet || item.completion.additionalTextEdits) { // snippet, other editor -> makes edit return true; } - const position = this._editor.getPosition(); + const position = this._editor.getPosition()!; const startColumn = item.completion.range.startColumn; const endColumn = position.column; if (endColumn - startColumn !== item.completion.insertText.length) { // unequal lengths -> makes edit return true; } - const textNow = this._editor.getModel().getValueInRange({ + const textNow = this._editor.getModel()!.getValueInRange({ startLineNumber: position.lineNumber, startColumn, endLineNumber: position.lineNumber, @@ -352,7 +354,7 @@ export class SuggestController implements IEditorContribution { fallback(); return; } - const index = this._memoryService.select(this._editor.getModel(), this._editor.getPosition(), completionModel.items); + const index = this._memoryService.select(this._editor.getModel()!, this._editor.getPosition()!, completionModel.items); const item = completionModel.items[index]; if (!makesTextEdit(item)) { fallback(); From d3024ec0c4eb830eb22a39bc2f2feacd314a952a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 15 Jan 2019 17:06:15 -0800 Subject: [PATCH 0281/1066] more strict null --- src/tsconfig.strictNullChecks.json | 11 ++--------- .../contrib/snippet/test/snippetSession.test.ts | 10 +++++----- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index f97a1fbcac7..55f5e2c88d6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -16,7 +16,8 @@ "./vs/editor/contrib/format/**/*.ts", "./vs/editor/contrib/gotoError/**/*.ts", "./vs/editor/contrib/inPlaceReplace/**/*.ts", - "./vs/editor/contrib/suggest/**/*.ts" + "./vs/editor/contrib/suggest/**/*.ts", + "./vs/editor/contrib/snippet/**/*.ts", ], "files": [ "./vs/base/parts/contextmenu/common/contextmenu.ts", @@ -243,14 +244,6 @@ "./vs/editor/contrib/referenceSearch/test/referencesModel.test.ts", "./vs/editor/contrib/rename/rename.ts", "./vs/editor/contrib/rename/renameInputField.ts", - "./vs/editor/contrib/snippet/snippetController2.ts", - "./vs/editor/contrib/snippet/snippetParser.ts", - "./vs/editor/contrib/snippet/snippetSession.ts", - "./vs/editor/contrib/snippet/snippetVariables.ts", - "./vs/editor/contrib/snippet/test/snippetController2.old.test.ts", - "./vs/editor/contrib/snippet/test/snippetController2.test.ts", - "./vs/editor/contrib/snippet/test/snippetParser.test.ts", - "./vs/editor/contrib/snippet/test/snippetVariables.test.ts", "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", "./vs/editor/contrib/wordOperations/test/wordOperations.test.ts", diff --git a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/snippetSession.test.ts index c6b9a5efd7c..0d2ec457b50 100644 --- a/src/vs/editor/contrib/snippet/test/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetSession.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 { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -14,11 +14,11 @@ import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; suite('SnippetSession', function () { - let editor: ICodeEditor; + let editor: IActiveCodeEditor; let model: TextModel; - function assertSelections(editor: ICodeEditor, ...s: Selection[]) { - for (const selection of editor.getSelections()!) { + function assertSelections(editor: IActiveCodeEditor, ...s: Selection[]) { + for (const selection of editor.getSelections()) { const actual = s.shift()!; assert.ok(selection.equalsSelection(actual), `actual=${selection.toString()} <> expected=${actual.toString()}`); } @@ -27,7 +27,7 @@ suite('SnippetSession', function () { setup(function () { model = TextModel.createFromString('function foo() {\n console.log(a);\n}'); - editor = createTestCodeEditor({ model: model }); + editor = createTestCodeEditor({ model: model }) as IActiveCodeEditor; editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 5, 2, 5)]); assert.equal(model.getEOL(), '\n'); }); From ac0a708d08d9d92d744826027841befdab69efa5 Mon Sep 17 00:00:00 2001 From: Rich Evans Date: Wed, 16 Jan 2019 00:02:26 -0800 Subject: [PATCH 0282/1066] 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 d19a07090d51c71e2be05cb99923cc6bdb3e4a79 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 16 Jan 2019 09:25:12 +0100 Subject: [PATCH 0283/1066] add treeview to strict null checks fixes #66557 --- src/tsconfig.strictNullChecks.json | 4 +- .../base/parts/tree/browser/treeViewModel.ts | 55 +++++++++---------- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 55f5e2c88d6..d37bcefd81b 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -42,7 +42,9 @@ "./vs/base/parts/tree/browser/treeDnd.ts", "./vs/base/parts/tree/browser/treeModel.ts", "./vs/base/parts/tree/browser/treeUtils.ts", + "./vs/base/parts/tree/browser/treeViewModel.ts", "./vs/base/parts/tree/test/browser/treeModel.test.ts", + "./vs/base/parts/tree/test/browser/treeViewModel.test.ts", "./vs/base/test/browser/browser.test.ts", "./vs/base/test/browser/comparers.test.ts", "./vs/base/test/browser/dom.test.ts", @@ -868,4 +870,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} +} \ No newline at end of file diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts index 07f0c852e3a..823f0bb0a9d 100644 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ b/src/vs/base/parts/tree/browser/treeViewModel.ts @@ -15,21 +15,16 @@ export interface IViewItem { export class HeightMap { - private heightMap: IViewItem[]; - private indexes: { [item: string]: number; }; + private heightMap: IViewItem[] = []; + private indexes: { [item: string]: number; } = {}; - constructor() { - this.heightMap = []; - this.indexes = {}; - } - - public getContentHeight(): number { + getContentHeight(): number { let last = this.heightMap[this.heightMap.length - 1]; return !last ? 0 : last.top + last.height; } - public onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number { - let item: Item; + onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { + let item: Item | null = null; let viewItem: IViewItem; let i: number, j: number; let totalSize: number; @@ -82,16 +77,16 @@ export class HeightMap { return sizeDiff; } - public onInsertItem(item: IViewItem): void { + onInsertItem(item: IViewItem): void { // noop } // Contiguous items - public onRemoveItems(iterator: INextIterator): void { - let itemId: string; + onRemoveItems(iterator: INextIterator): void { + let itemId: string | null = null; let viewItem: IViewItem; let startIndex: number | null = null; - let i: number; + let i = 0; let sizeDiff = 0; while (itemId = iterator.next()) { @@ -112,7 +107,7 @@ export class HeightMap { } } - if (sizeDiff === 0) { + if (sizeDiff === 0 || startIndex === null) { return; } @@ -126,18 +121,18 @@ export class HeightMap { } } - public onRemoveItem(item: IViewItem): void { + onRemoveItem(item: IViewItem): void { // noop } - public onRefreshItemSet(items: Item[]): void { + onRefreshItemSet(items: Item[]): void { let sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); this.onRefreshItems(new ArrayIterator(sortedItems)); } // Ordered, but not necessarily contiguous items - public onRefreshItems(iterator: INextIterator): void { - let item: Item; + onRefreshItems(iterator: INextIterator): void { + let item: Item | null = null; let viewItem: IViewItem; let newHeight: number; let i: number, j: number | null = null; @@ -171,19 +166,19 @@ export class HeightMap { } } - public onRefreshItem(item: IViewItem, needsRender: boolean = false): void { + onRefreshItem(item: IViewItem, needsRender: boolean = false): void { // noop } - public itemsCount(): number { + itemsCount(): number { return this.heightMap.length; } - public itemAt(position: number): string { + itemAt(position: number): string { return this.heightMap[this.indexAt(position)].model.id; } - public withItemsInRange(start: number, end: number, fn: (item: string) => void): void { + withItemsInRange(start: number, end: number, fn: (item: string) => void): void { start = this.indexAt(start); end = this.indexAt(end); for (let i = start; i <= end; i++) { @@ -191,7 +186,7 @@ export class HeightMap { } } - public indexAt(position: number): number { + indexAt(position: number): number { let left = 0; let right = this.heightMap.length; let center: number; @@ -217,15 +212,15 @@ export class HeightMap { return this.heightMap.length; } - public indexAfter(position: number): number { + indexAfter(position: number): number { return Math.min(this.indexAt(position) + 1, this.heightMap.length); } - public itemAtIndex(index: number): IViewItem { + itemAtIndex(index: number): IViewItem { return this.heightMap[index]; } - public itemAfter(item: IViewItem): IViewItem { + itemAfter(item: IViewItem): IViewItem { return this.heightMap[this.indexes[item.model.id] + 1] || null; } @@ -233,8 +228,8 @@ export class HeightMap { throw new Error('not implemented'); } - public dispose(): void { - this.heightMap = null; - this.indexes = null; + dispose(): void { + this.heightMap = []; + this.indexes = {}; } } From f1c9ab69c30cbd68977364ece822b2d2860a366f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 16 Jan 2019 09:45:45 +0100 Subject: [PATCH 0284/1066] strict null: add treeView.ts --- src/tsconfig.strictNullChecks.json | 1 + src/vs/base/parts/tree/browser/treeView.ts | 199 ++++++++++----------- 2 files changed, 94 insertions(+), 106 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index d37bcefd81b..eb1e4a4f320 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -42,6 +42,7 @@ "./vs/base/parts/tree/browser/treeDnd.ts", "./vs/base/parts/tree/browser/treeModel.ts", "./vs/base/parts/tree/browser/treeUtils.ts", + "./vs/base/parts/tree/browser/treeView.ts", "./vs/base/parts/tree/browser/treeViewModel.ts", "./vs/base/parts/tree/test/browser/treeModel.test.ts", "./vs/base/parts/tree/test/browser/treeViewModel.test.ts", diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 4c04f288b19..110ff2f8e53 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -26,14 +26,14 @@ import { DefaultTreestyler } from './treeDefaults'; import { Delayer, timeout } from 'vs/base/common/async'; export interface IRow { - element: HTMLElement; + element: HTMLElement | null; templateId: string; templateData: any; } function removeFromParent(element: HTMLElement): void { try { - element.parentElement.removeChild(element); + element.parentElement!.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } @@ -41,7 +41,7 @@ function removeFromParent(element: HTMLElement): void { export class RowCache implements Lifecycle.IDisposable { - private _cache: { [templateId: string]: IRow[]; }; + private _cache: { [templateId: string]: IRow[]; } | null; constructor(private context: _.ITreeContext) { this._cache = { '': [] }; @@ -60,7 +60,7 @@ export class RowCache implements Lifecycle.IDisposable { let templateData: any = null; try { - templateData = this.context.renderer.renderTemplate(this.context.tree, templateId, content); + templateData = this.context.renderer!.renderTemplate(this.context.tree, templateId, content); } catch (err) { console.error('Tree usage error: exception while rendering template'); console.error(err); @@ -77,24 +77,24 @@ export class RowCache implements Lifecycle.IDisposable { } public release(templateId: string, row: IRow): void { - removeFromParent(row.element); + removeFromParent(row.element!); this.cache(templateId).push(row); } private cache(templateId: string): IRow[] { - return this._cache[templateId] || (this._cache[templateId] = []); + return this._cache![templateId] || (this._cache![templateId] = []); } public garbageCollect(): void { if (this._cache) { Object.keys(this._cache).forEach(templateId => { - this._cache[templateId].forEach(cachedRow => { - this.context.renderer.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); + this._cache![templateId].forEach(cachedRow => { + this.context.renderer!.disposeTemplate(this.context.tree, templateId, cachedRow.templateData); cachedRow.element = null; cachedRow.templateData = null; }); - delete this._cache[templateId]; + delete this._cache![templateId]; }); } } @@ -102,7 +102,6 @@ export class RowCache implements Lifecycle.IDisposable { public dispose(): void { this.garbageCollect(); this._cache = null; - this.context = null; } } @@ -117,7 +116,7 @@ export class ViewItem implements IViewItem { public model: Model.Item; public id: string; - protected row: IRow; + protected row: IRow | null; public top: number; public height: number; @@ -125,8 +124,8 @@ export class ViewItem implements IViewItem { public onDragStart: (e: DragEvent) => void; public needsRender: boolean; - public uri: string; - public unbindDragStart: Lifecycle.IDisposable; + public uri: string | null; + public unbindDragStart: Lifecycle.IDisposable = Lifecycle.Disposable.None; public loadingTimer: any; public _styles: any; @@ -172,12 +171,12 @@ export class ViewItem implements IViewItem { } public get element(): HTMLElement { - return this.row && this.row.element; + return (this.row && this.row.element)!; } private _templateId: string; private get templateId(): string { - return this._templateId || (this._templateId = (this.context.renderer.getTemplateId && this.context.renderer.getTemplateId(this.context.tree, this.model.getElement()))); + return this._templateId || (this._templateId = (this.context.renderer!.getTemplateId && this.context.renderer!.getTemplateId(this.context.tree, this.model.getElement()))); } public addClass(name: string): void { @@ -208,7 +207,7 @@ export class ViewItem implements IViewItem { // ARIA this.element.setAttribute('role', 'treeitem'); - const accessibility = this.context.accessibilityProvider; + const accessibility = this.context.accessibilityProvider!; const ariaLabel = accessibility.getAriaLabel(this.context.tree, this.model.getElement()); if (ariaLabel) { this.element.setAttribute('aria-label', ariaLabel); @@ -234,18 +233,17 @@ export class ViewItem implements IViewItem { this.element.setAttribute('aria-level', String(this.model.getDepth())); if (this.context.options.paddingOnRow) { - this.element.style.paddingLeft = this.context.options.twistiePixels + ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; + this.element.style.paddingLeft = this.context.options.twistiePixels! + ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; } else { - this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels) + 'px'; - (this.row.element.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; + this.element.style.paddingLeft = ((this.model.getDepth() - 1) * this.context.options.indentPixels!) + 'px'; + (this.row!.element!.firstElementChild).style.paddingLeft = this.context.options.twistiePixels + 'px'; } - let uri = this.context.dnd.getDragURI(this.context.tree, this.model.getElement()); + let uri = this.context.dnd!.getDragURI(this.context.tree, this.model.getElement()); if (uri !== this.uri) { if (this.unbindDragStart) { this.unbindDragStart.dispose(); - this.unbindDragStart = null; } if (uri) { @@ -260,10 +258,10 @@ export class ViewItem implements IViewItem { } if (!skipUserRender && this.element) { - let paddingLeft: number | undefined; + let paddingLeft: number = 0; if (this.context.horizontalScrolling) { const style = window.getComputedStyle(this.element); - paddingLeft = parseFloat(style.paddingLeft); + paddingLeft = parseFloat(style.paddingLeft!); } if (this.context.horizontalScrolling) { @@ -271,7 +269,7 @@ export class ViewItem implements IViewItem { } try { - this.context.renderer.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row.templateData); + this.context.renderer!.renderElement(this.context.tree, this.model.getElement(), this.templateId, this.row!.templateData); } catch (err) { console.error('Tree usage error: exception while rendering element'); console.error(err); @@ -290,13 +288,13 @@ export class ViewItem implements IViewItem { } const style = window.getComputedStyle(this.element); - const paddingLeft = parseFloat(style.paddingLeft); + const paddingLeft = parseFloat(style.paddingLeft!); this.element.style.width = 'fit-content'; this.width = DOM.getContentWidth(this.element) + paddingLeft; this.element.style.width = ''; } - public insertInDOM(container: HTMLElement, afterElement: HTMLElement): void { + public insertInDOM(container: HTMLElement, afterElement: HTMLElement | null): void { if (!this.row) { this.row = this.context.cache.alloc(this.templateId); @@ -327,10 +325,7 @@ export class ViewItem implements IViewItem { return; } - if (this.unbindDragStart) { - this.unbindDragStart.dispose(); - this.unbindDragStart = null; - } + this.unbindDragStart.dispose(); this.uri = null; @@ -341,7 +336,6 @@ export class ViewItem implements IViewItem { public dispose(): void { this.row = null; - this.model = null; } } @@ -353,7 +347,7 @@ class RootViewItem extends ViewItem { this.row = { element: wrapper, templateData: null, - templateId: null + templateId: null! }; } @@ -386,7 +380,7 @@ interface IThrottledGestureEvent { translationY: number; } -function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction): boolean { +function reactionEquals(one: _.IDragOverReaction, other: _.IDragOverReaction | null): boolean { if (!one && !other) { return true; } else if (!one || !other) { @@ -412,7 +406,7 @@ export class TreeView extends HeightMap { private context: IViewContext; private modelListeners: Lifecycle.IDisposable[]; - private model: Model.TreeModel; + private model: Model.TreeModel | null = null; private viewListeners: Lifecycle.IDisposable[]; private domNode: HTMLElement; @@ -436,21 +430,21 @@ export class TreeView extends HeightMap { private isRefreshing = false; private refreshingPreviousChildrenIds: { [id: string]: string[] } = {}; - private currentDragAndDropData: IDragAndDropData; + private currentDragAndDropData: IDragAndDropData | null = null; private currentDropElement: any; private currentDropElementReaction: _.IDragOverReaction; - private currentDropTarget: ViewItem; + private currentDropTarget: ViewItem | null = null; private shouldInvalidateDropReaction: boolean; - private currentDropTargets: ViewItem[]; + private currentDropTargets: ViewItem[] | null = null; private currentDropDisposable: Lifecycle.IDisposable = Lifecycle.Disposable.None; - private dragAndDropScrollInterval: number; - private dragAndDropScrollTimeout: number; - private dragAndDropMouseY: number; + private dragAndDropScrollInterval: number | null = null; + private dragAndDropScrollTimeout: number | null = null; + private dragAndDropMouseY: number | null = null; private didJustPressContextMenuKey: boolean; private highlightedItemWasDraggable: boolean; - private onHiddenScrollTop: number; + private onHiddenScrollTop: number | null = null; private readonly _onDOMFocus = new Emitter(); get onDOMFocus(): Event { return this._onDOMFocus.event; } @@ -487,7 +481,6 @@ export class TreeView extends HeightMap { this.modelListeners = []; this.viewListeners = []; - this.model = null; this.items = {}; this.domNode = document.createElement('div'); @@ -497,10 +490,7 @@ export class TreeView extends HeightMap { this.styleElement = DOM.createStyleSheet(this.domNode); - this.treeStyler = context.styler; - if (!this.treeStyler) { - this.treeStyler = new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); - } + this.treeStyler = context.styler || new DefaultTreestyler(this.styleElement, `monaco-tree-instance-${this.instance}`); // ARIA this.domNode.setAttribute('role', 'tree'); @@ -602,8 +592,6 @@ export class TreeView extends HeightMap { this.dragAndDropScrollInterval = null; this.dragAndDropScrollTimeout = null; - this.onHiddenScrollTop = null; - this.onRowsChanged(); this.layout(); @@ -637,7 +625,7 @@ export class TreeView extends HeightMap { } public onVisible(): void { - this.scrollTop = this.onHiddenScrollTop; + this.scrollTop = this.onHiddenScrollTop!; this.onHiddenScrollTop = null; this.setupMSGesture(); } @@ -794,10 +782,10 @@ export class TreeView extends HeightMap { let lastPageIndex = this.indexAt(this.scrollTop + this.viewHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; let lastPageElement = this.itemAtIndex(lastPageIndex).model.getElement(); - let currentlyFocusedElement = this.model.getFocus(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== lastPageElement) { - this.model.setFocus(lastPageElement, eventPayload); + this.model!.setFocus(lastPageElement, eventPayload); } else { let previousScrollTop = this.scrollTop; this.scrollTop += this.viewHeight; @@ -822,10 +810,10 @@ export class TreeView extends HeightMap { } let firstPageElement = this.itemAtIndex(firstPageIndex).model.getElement(); - let currentlyFocusedElement = this.model.getFocus(); + let currentlyFocusedElement = this.model!.getFocus(); if (currentlyFocusedElement !== firstPageElement) { - this.model.setFocus(firstPageElement, eventPayload); + this.model!.setFocus(firstPageElement, eventPayload); } else { let previousScrollTop = this.scrollTop; this.scrollTop -= this.viewHeight; @@ -917,7 +905,7 @@ export class TreeView extends HeightMap { if (!e.isNested) { let childrenIds: string[] = []; let navigator = item.getNavigator(); - let childItem: Model.Item; + let childItem: Model.Item | null; while (childItem = navigator.next()) { childrenIds.push(childItem.id); @@ -944,15 +932,15 @@ export class TreeView extends HeightMap { let previousChildrenIds = this.refreshingPreviousChildrenIds[item.id]; let afterModelItems: Model.Item[] = []; let navigator = item.getNavigator(); - let childItem: Model.Item; + let childItem: Model.Item | null; while (childItem = navigator.next()) { afterModelItems.push(childItem); } let skipDiff = Math.abs(previousChildrenIds.length - afterModelItems.length) > 1000; - let diff: Diff.IDiffChange[]; - let doToInsertItemsAlreadyExist: boolean; + let diff: Diff.IDiffChange[] = []; + let doToInsertItemsAlreadyExist: boolean = false; if (!skipDiff) { const lcs = new Diff.LcsDiff( @@ -994,7 +982,7 @@ export class TreeView extends HeightMap { } if (diffChange.modifiedLength > 0) { - let beforeItem = afterModelItems[diffChange.modifiedStart - 1] || item; + let beforeItem: Model.Item | null = afterModelItems[diffChange.modifiedStart - 1] || item; beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null; this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); @@ -1034,7 +1022,7 @@ export class TreeView extends HeightMap { if (viewItem) { viewItem.expanded = true; - let height = this.onInsertItems(item.getNavigator(), item.id); + let height = this.onInsertItems(item.getNavigator(), item.id) || 0; let scrollTop = this.scrollTop; if (viewItem.top + viewItem.height <= this.scrollTop) { @@ -1211,11 +1199,11 @@ export class TreeView extends HeightMap { } this.lastClickTimeStamp = Date.now(); - this.context.controller.onClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onClick(this.context.tree, item.model.getElement(), event); } private onMouseMiddleClick(e: MouseEvent): void { - if (!this.context.controller.onMouseMiddleClick) { + if (!this.context.controller!.onMouseMiddleClick!) { return; } @@ -1225,13 +1213,13 @@ export class TreeView extends HeightMap { if (!item) { return; } - this.context.controller.onMouseMiddleClick(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseMiddleClick!(this.context.tree, item.model.getElement(), event); } private onMouseDown(e: MouseEvent): void { this.didJustPressContextMenuKey = false; - if (!this.context.controller.onMouseDown) { + if (!this.context.controller!.onMouseDown!) { return; } @@ -1251,11 +1239,11 @@ export class TreeView extends HeightMap { return; } - this.context.controller.onMouseDown(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseDown!(this.context.tree, item.model.getElement(), event); } private onMouseUp(e: MouseEvent): void { - if (!this.context.controller.onMouseUp) { + if (!this.context.controller!.onMouseUp!) { return; } @@ -1275,7 +1263,7 @@ export class TreeView extends HeightMap { return; } - this.context.controller.onMouseUp(this.context.tree, item.model.getElement(), event); + this.context.controller!.onMouseUp!(this.context.tree, item.model.getElement(), event); } private onTap(e: Touch.GestureEvent): void { @@ -1285,7 +1273,7 @@ export class TreeView extends HeightMap { return; } - this.context.controller.onTap(this.context.tree, item.model.getElement(), e); + this.context.controller!.onTap(this.context.tree, item.model.getElement(), e); } private onTouchChange(event: Touch.GestureEvent): void { @@ -1305,12 +1293,12 @@ export class TreeView extends HeightMap { this.didJustPressContextMenuKey = false; let keyboardEvent = new Keyboard.StandardKeyboardEvent(event); - element = this.model.getFocus(); + element = this.model!.getFocus(); let position: DOM.IDomNodePagePosition; if (!element) { - element = this.model.getInput(); + element = this.model!.getInput(); position = DOM.getDomNodePagePosition(this.inputItem.element); } else { let id = this.context.dataSource.getId(this.context.tree, element); @@ -1332,7 +1320,7 @@ export class TreeView extends HeightMap { resultEvent = new _.MouseContextMenuEvent(mouseEvent); } - this.context.controller.onContextMenu(this.context.tree, element, resultEvent); + this.context.controller!.onContextMenu(this.context.tree, element, resultEvent); } private onKeyDown(e: KeyboardEvent): void { @@ -1349,7 +1337,7 @@ export class TreeView extends HeightMap { event.stopPropagation(); } - this.context.controller.onKeyDown(this.context.tree, event); + this.context.controller!.onKeyDown(this.context.tree, event); } private onKeyUp(e: KeyboardEvent): void { @@ -1358,16 +1346,16 @@ export class TreeView extends HeightMap { } this.didJustPressContextMenuKey = false; - this.context.controller.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); + this.context.controller!.onKeyUp(this.context.tree, new Keyboard.StandardKeyboardEvent(e)); } private onDragStart(item: ViewItem, e: any): void { - if (this.model.getHighlight()) { + if (this.model!.getHighlight()) { return; } let element = item.model.getElement(); - let selection = this.model.getSelection(); + let selection = this.model!.getSelection(); let elements: any[]; if (selection.indexOf(element) > -1) { @@ -1381,8 +1369,8 @@ export class TreeView extends HeightMap { if (e.dataTransfer.setDragImage) { let label: string; - if (this.context.dnd.getDragLabel) { - label = this.context.dnd.getDragLabel(this.context.tree, elements); + if (this.context.dnd!.getDragLabel) { + label = this.context.dnd!.getDragLabel!(this.context.tree, elements); } else { label = String(elements.length); } @@ -1398,7 +1386,7 @@ export class TreeView extends HeightMap { this.currentDragAndDropData = new dnd.ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new dnd.ExternalElementsDragAndDropData(elements); - this.context.dnd.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); + this.context.dnd!.onDragStart(this.context.tree, this.currentDragAndDropData, new Mouse.DragMouseEvent(e)); } private setupDragAndDropScrollInterval(): void { @@ -1406,7 +1394,7 @@ export class TreeView extends HeightMap { if (!this.dragAndDropScrollInterval) { this.dragAndDropScrollInterval = window.setInterval(() => { - if (this.dragAndDropMouseY === undefined) { + if (this.dragAndDropMouseY === null) { return; } @@ -1459,7 +1447,7 @@ export class TreeView extends HeightMap { if (this.currentDropTarget) { // clear previously hovered element feedback - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } @@ -1490,16 +1478,16 @@ export class TreeView extends HeightMap { } } - this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer); + this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer!); let element: any; - let item: Model.Item = viewItem.model; - let reaction: _.IDragOverReaction; + let item: Model.Item | null = viewItem.model; + let reaction: _.IDragOverReaction | null; // check the bubble up behavior do { - element = item ? item.getElement() : this.model.getInput(); - reaction = this.context.dnd.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); + element = item ? item.getElement() : this.model!.getInput(); + reaction = this.context.dnd!.onDragOver(this.context.tree, this.currentDragAndDropData, element, event); if (!reaction || reaction.bubble !== _.DragOverBubble.BUBBLE_UP) { break; @@ -1518,7 +1506,7 @@ export class TreeView extends HeightMap { if (canDrop) { this.currentDropElement = item.getElement(); event.preventDefault(); - event.dataTransfer.dropEffect = reaction.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; + event.dataTransfer.dropEffect = reaction!.effect === _.DragOverEffect.COPY ? 'copy' : 'move'; } else { this.currentDropElement = null; } @@ -1532,35 +1520,35 @@ export class TreeView extends HeightMap { this.shouldInvalidateDropReaction = false; if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; this.currentDropDisposable.dispose(); } this.currentDropTarget = currentDropTarget; - this.currentDropElementReaction = reaction; + this.currentDropElementReaction = reaction!; if (canDrop) { // setup hover feedback for drop target if (this.currentDropTarget) { this.currentDropTarget.dropTarget = true; - this.currentDropTargets.push(this.currentDropTarget); + this.currentDropTargets!.push(this.currentDropTarget); } - if (reaction.bubble === _.DragOverBubble.BUBBLE_DOWN) { + if (reaction!.bubble === _.DragOverBubble.BUBBLE_DOWN) { let nav = item.getNavigator(); - let child: Model.Item; + let child: Model.Item | null; while (child = nav.next()) { viewItem = this.items[child.id]; if (viewItem) { viewItem.dropTarget = true; - this.currentDropTargets.push(viewItem); + this.currentDropTargets!.push(viewItem); } } } - if (reaction.autoExpand) { + if (reaction!.autoExpand) { const timeoutPromise = timeout(500); this.currentDropDisposable = Lifecycle.toDisposable(() => timeoutPromise.cancel()); @@ -1578,8 +1566,8 @@ export class TreeView extends HeightMap { if (this.currentDropElement) { let event = new Mouse.DragMouseEvent(e); event.preventDefault(); - this.currentDragAndDropData.update((event.browserEvent as DragEvent).dataTransfer); - this.context.dnd.drop(this.context.tree, this.currentDragAndDropData, this.currentDropElement, event); + this.currentDragAndDropData!.update((event.browserEvent as DragEvent).dataTransfer!); + this.context.dnd!.drop(this.context.tree, this.currentDragAndDropData!, this.currentDropElement, event); this.onDragEnd(e); } this.cancelDragAndDropScrollInterval(); @@ -1587,7 +1575,7 @@ export class TreeView extends HeightMap { private onDragEnd(e: DragEvent): void { if (this.currentDropTarget) { - this.currentDropTargets.forEach(i => i.dropTarget = false); + this.currentDropTargets!.forEach(i => i.dropTarget = false); this.currentDropTargets = []; } @@ -1679,21 +1667,24 @@ export class TreeView extends HeightMap { return item.top < this.lastRenderTop + this.lastRenderHeight && item.top + item.height > this.lastRenderTop; } - private getItemAround(element: HTMLElement): ViewItem { + private getItemAround(element: HTMLElement): ViewItem | undefined { let candidate: ViewItem = this.inputItem; + let el: HTMLElement | null = element; + do { - if ((element)[TreeView.BINDING]) { - candidate = (element)[TreeView.BINDING]; + if ((el)[TreeView.BINDING]) { + candidate = (el)[TreeView.BINDING]; } - if (element === this.wrapper || element === this.domNode) { + if (el === this.wrapper || el === this.domNode) { return candidate; } - if (element === this.scrollableElement.getDomNode() || element === document.body) { - return null; + if (el === this.scrollableElement.getDomNode() || el === document.body) { + return undefined; } - } while (element = element.parentElement); + } while (el = el.parentElement); + return undefined; } @@ -1711,7 +1702,6 @@ export class TreeView extends HeightMap { this.scrollableElement.dispose(); this.releaseModel(); - this.modelListeners = null; this.viewListeners = Lifecycle.dispose(this.viewListeners); @@ -1721,16 +1711,13 @@ export class TreeView extends HeightMap { if (this.domNode.parentNode) { this.domNode.parentNode.removeChild(this.domNode); } - this.domNode = null; if (this.items) { Object.keys(this.items).forEach(key => this.items[key].removeFromDOM()); - this.items = null; } if (this.context.cache) { this.context.cache.dispose(); - this.context.cache = null; } super.dispose(); From 2b941450a4926613b1b3ccb9c552ee6bede47b7a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 16 Jan 2019 09:58:23 +0100 Subject: [PATCH 0285/1066] search: throw error instead of returning null related to #66573 --- src/vs/workbench/parts/search/browser/searchResultsView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 226cf38c68e..01ced65b820 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -65,7 +65,8 @@ export class SearchDelegate implements IListVirtualDelegate { return MatchRenderer.TEMPLATE_ID; } - return null; + console.error('Invalid search tree element', element); + throw new Error('Invalid search tree element'); } } From 420067816b5c248dee2dcdb26e26180671dcc6e8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 16 Jan 2019 10:02:23 +0100 Subject: [PATCH 0286/1066] Make the keybindings extension point dynamic (#66574) --- .../keybinding/common/keybindingsRegistry.ts | 71 +++++++++++-------- .../electron-browser/keybindingService.ts | 28 +++----- 2 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/vs/platform/keybinding/common/keybindingsRegistry.ts b/src/vs/platform/keybinding/common/keybindingsRegistry.ts index 6121df6a4ef..57bf20e8a73 100644 --- a/src/vs/platform/keybinding/common/keybindingsRegistry.ts +++ b/src/vs/platform/keybinding/common/keybindingsRegistry.ts @@ -52,11 +52,6 @@ export interface IKeybindingRule2 { when: ContextKeyExpr | null; } -export const enum KeybindingRuleSource { - Core = 0, - Extension = 1 -} - export const enum KeybindingWeight { EditorCore = 0, EditorContrib = 100, @@ -71,20 +66,22 @@ export interface ICommandAndKeybindingRule extends IKeybindingRule { } export interface IKeybindingsRegistry { - registerKeybindingRule(rule: IKeybindingRule, source?: KeybindingRuleSource): void; - registerKeybindingRule2(rule: IKeybindingRule2, source?: KeybindingRuleSource): void; - registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule, source?: KeybindingRuleSource): void; + registerKeybindingRule(rule: IKeybindingRule): void; + setExtensionKeybindings(rules: IKeybindingRule2[]): void; + registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void; getDefaultKeybindings(): IKeybindingItem[]; } class KeybindingsRegistryImpl implements IKeybindingsRegistry { - private _keybindings: IKeybindingItem[]; - private _keybindingsSorted: boolean; + private _coreKeybindings: IKeybindingItem[]; + private _extensionKeybindings: IKeybindingItem[]; + private _cachedMergedKeybindings: IKeybindingItem[] | null; constructor() { - this._keybindings = []; - this._keybindingsSorted = true; + this._coreKeybindings = []; + this._extensionKeybindings = []; + this._cachedMergedKeybindings = null; } /** @@ -129,13 +126,13 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { return kb; } - public registerKeybindingRule(rule: IKeybindingRule, source: KeybindingRuleSource = KeybindingRuleSource.Core): void { - let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule); + public registerKeybindingRule(rule: IKeybindingRule): void { + const actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform(rule); if (actualKb && actualKb.primary) { const kk = createKeybinding(actualKb.primary, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, 0, rule.when, source); + this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, 0, rule.when); } } @@ -144,22 +141,36 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { const k = actualKb.secondary[i]; const kk = createKeybinding(k, OS); if (kk) { - this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, -i - 1, rule.when, source); + this._registerDefaultKeybinding(kk, rule.id, undefined, rule.weight, -i - 1, rule.when); } } } } - public registerKeybindingRule2(rule: IKeybindingRule2, source: KeybindingRuleSource = KeybindingRuleSource.Core): void { - let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform2(rule); + public setExtensionKeybindings(rules: IKeybindingRule2[]): void { + let result: IKeybindingItem[] = [], keybindingsLen = 0; + for (let i = 0, len = rules.length; i < len; i++) { + const rule = rules[i]; + let actualKb = KeybindingsRegistryImpl.bindToCurrentPlatform2(rule); - if (actualKb && actualKb.primary) { - this._registerDefaultKeybinding(actualKb.primary, rule.id, rule.args, rule.weight, 0, rule.when, source); + if (actualKb && actualKb.primary) { + result[keybindingsLen++] = { + keybinding: actualKb.primary, + command: rule.id, + commandArgs: rule.args, + when: rule.when, + weight1: rule.weight, + weight2: 0 + }; + } } + + this._extensionKeybindings = result; + this._cachedMergedKeybindings = null; } - public registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule, source: KeybindingRuleSource = KeybindingRuleSource.Core): void { - this.registerKeybindingRule(desc, source); + public registerCommandAndKeybindingRule(desc: ICommandAndKeybindingRule): void { + this.registerKeybindingRule(desc); CommandsRegistry.registerCommand(desc); } @@ -197,15 +208,15 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { } } - private _registerDefaultKeybinding(keybinding: Keybinding, commandId: string, commandArgs: any, weight1: number, weight2: number, when: ContextKeyExpr | null | undefined, source: KeybindingRuleSource): void { - if (source === KeybindingRuleSource.Core && OS === OperatingSystem.Windows) { + private _registerDefaultKeybinding(keybinding: Keybinding, commandId: string, commandArgs: any, weight1: number, weight2: number, when: ContextKeyExpr | null | undefined): void { + if (OS === OperatingSystem.Windows) { if (keybinding.type === KeybindingType.Chord) { this._assertNoCtrlAlt(keybinding.firstPart, commandId); } else { this._assertNoCtrlAlt(keybinding, commandId); } } - this._keybindings.push({ + this._coreKeybindings.push({ keybinding: keybinding, command: commandId, commandArgs: commandArgs, @@ -213,15 +224,15 @@ class KeybindingsRegistryImpl implements IKeybindingsRegistry { weight1: weight1, weight2: weight2 }); - this._keybindingsSorted = false; + this._cachedMergedKeybindings = null; } public getDefaultKeybindings(): IKeybindingItem[] { - if (!this._keybindingsSorted) { - this._keybindings.sort(sorter); - this._keybindingsSorted = true; + if (!this._cachedMergedKeybindings) { + this._cachedMergedKeybindings = ([]).concat(this._coreKeybindings).concat(this._extensionKeybindings); + this._cachedMergedKeybindings.sort(sorter); } - return this._keybindings.slice(0); + return this._cachedMergedKeybindings.slice(0); } } export const KeybindingsRegistry: IKeybindingsRegistry = new KeybindingsRegistryImpl(); diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index ea6946f0742..24399d5b830 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -24,7 +24,7 @@ import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/c import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { IKeybindingEvent, IKeyboardEvent, IUserFriendlyKeybinding, KeybindingSource } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; -import { IKeybindingItem, IKeybindingRule2, KeybindingRuleSource, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { IKeybindingItem, IKeybindingRule2, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -236,6 +236,7 @@ let keybindingType: IJSONSchema = { }; const keybindingsExtPoint = ExtensionsRegistry.registerExtensionPoint({ + isDynamic: true, extensionPoint: 'keybindings', jsonSchema: { description: nls.localize('vscode.extension.contributes.keybindings', "Contributes keybindings."), @@ -304,15 +305,14 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this.userKeybindings = this._register(new ConfigWatcher(environmentService.appKeybindingsPath, { defaultConfig: [], onError: error => onUnexpectedError(error) })); keybindingsExtPoint.setHandler((extensions) => { - let commandAdded = false; + let keybindings: IKeybindingRule2[] = []; for (let extension of extensions) { - commandAdded = this._handleKeybindingsExtensionPointUser(extension.description.isBuiltin, extension.value, extension.collector) || commandAdded; + this._handleKeybindingsExtensionPointUser(extension.description.isBuiltin, extension.value, extension.collector, keybindings); } - if (commandAdded) { - this.updateResolver({ source: KeybindingSource.Default }); - } + KeybindingsRegistry.setExtensionKeybindings(keybindings); + this.updateResolver({ source: KeybindingSource.Default }); }); this._register(this.userKeybindings.onDidUpdateConfiguration(event => this.updateResolver({ @@ -453,28 +453,24 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { return this._keyboardMapper.resolveUserBinding(firstPart, chordPart); } - private _handleKeybindingsExtensionPointUser(isBuiltin: boolean, keybindings: ContributedKeyBinding | ContributedKeyBinding[], collector: ExtensionMessageCollector): boolean { + private _handleKeybindingsExtensionPointUser(isBuiltin: boolean, keybindings: ContributedKeyBinding | ContributedKeyBinding[], collector: ExtensionMessageCollector, result: IKeybindingRule2[]): void { if (isContributedKeyBindingsArray(keybindings)) { - let commandAdded = false; for (let i = 0, len = keybindings.length; i < len; i++) { - commandAdded = this._handleKeybinding(isBuiltin, i + 1, keybindings[i], collector) || commandAdded; + this._handleKeybinding(isBuiltin, i + 1, keybindings[i], collector, result); } - return commandAdded; } else { - return this._handleKeybinding(isBuiltin, 1, keybindings, collector); + this._handleKeybinding(isBuiltin, 1, keybindings, collector, result); } } - private _handleKeybinding(isBuiltin: boolean, idx: number, keybindings: ContributedKeyBinding, collector: ExtensionMessageCollector): boolean { + private _handleKeybinding(isBuiltin: boolean, idx: number, keybindings: ContributedKeyBinding, collector: ExtensionMessageCollector, result: IKeybindingRule2[]): void { let rejects: string[] = []; - let commandAdded = false; if (isValidContributedKeyBinding(keybindings, rejects)) { let rule = this._asCommandRule(isBuiltin, idx++, keybindings); if (rule) { - KeybindingsRegistry.registerKeybindingRule2(rule, KeybindingRuleSource.Extension); - commandAdded = true; + result.push(rule); } } @@ -486,8 +482,6 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { rejects.join('\n') )); } - - return commandAdded; } private _asCommandRule(isBuiltin: boolean, idx: number, binding: ContributedKeyBinding): IKeybindingRule2 | undefined { From c3fbe63ff543bf211a1b8a2ea2c86a0b5b255728 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 16 Jan 2019 09:47:33 +0000 Subject: [PATCH 0287/1066] Listen on formatter registrations (#489) --- .../page/electron-browser/welcomePage.ts | 139 ++++++++++-------- 1 file changed, 76 insertions(+), 63 deletions(-) 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 8618298189e..9185347a099 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -312,70 +312,17 @@ class WelcomePage { if (!ul) { return; } - const before = ul.firstElementChild; - workspaces.slice(0, 5).forEach(workspace => { - let label: string; - let resource: URI; - if (isSingleFolderWorkspaceIdentifier(workspace)) { - resource = workspace; - label = this.labelService.getWorkspaceLabel(workspace); - } else if (isWorkspaceIdentifier(workspace)) { - label = this.labelService.getWorkspaceLabel(workspace); - resource = URI.file(workspace.configPath); - } else { - label = getBaseLabel(workspace); - resource = URI.file(workspace); + const moreRecent = ul.querySelector('.moreRecent'); + const workspacesToShow = workspaces.slice(0, 5); + const updateEntries = () => { + const listEntries = this.createListEntries(workspacesToShow); + while (ul.firstChild) { + ul.removeChild(ul.firstChild); } - - const li = document.createElement('li'); - - const a = document.createElement('a'); - let name = label; - let parentFolderPath: string | undefined; - - if (resource.scheme === Schemas.file) { - let parentFolder = path.dirname(resource.fsPath); - if (!name && parentFolder) { - const tmp = name; - name = parentFolder; - parentFolder = tmp; - } - parentFolderPath = tildify(parentFolder, this.environmentService.userHome); - } else { - parentFolderPath = this.labelService.getUriLabel(resource); - } - - - a.innerText = name; - a.title = label; - a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentFolderPath)); - a.href = 'javascript:void(0)'; - a.addEventListener('click', e => { - /* __GDPR__ - "workbenchActionExecuted" : { - "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('workbenchActionExecuted', { - id: 'openRecentFolder', - from: telemetryFrom - }); - this.windowService.openWindow([resource], { forceNewWindow: e.ctrlKey || e.metaKey }); - e.preventDefault(); - e.stopPropagation(); - }); - li.appendChild(a); - - const span = document.createElement('span'); - span.classList.add('path'); - span.classList.add('detail'); - span.innerText = parentFolderPath; - span.title = label; - li.appendChild(span); - - ul.insertBefore(li, before); - }); + ul.append(...listEntries, moreRecent); + }; + updateEntries(); + this.disposables.push(this.labelService.onDidRegisterFormatter(updateEntries)); }).then(undefined, onUnexpectedError); this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings); @@ -393,6 +340,72 @@ class WelcomePage { })); } + private createListEntries(workspaces: (URI | IWorkspaceIdentifier)[]) { + return workspaces.map(workspace => { + let label: string; + let resource: URI; + if (isSingleFolderWorkspaceIdentifier(workspace)) { + resource = workspace; + label = this.labelService.getWorkspaceLabel(workspace); + } else if (isWorkspaceIdentifier(workspace)) { + label = this.labelService.getWorkspaceLabel(workspace); + resource = URI.file(workspace.configPath); + } else { + label = getBaseLabel(workspace); + resource = URI.file(workspace); + } + + const li = document.createElement('li'); + + const a = document.createElement('a'); + let name = label; + let parentFolderPath: string | undefined; + + if (resource.scheme === Schemas.file) { + let parentFolder = path.dirname(resource.fsPath); + if (!name && parentFolder) { + const tmp = name; + name = parentFolder; + parentFolder = tmp; + } + parentFolderPath = tildify(parentFolder, this.environmentService.userHome); + } else { + parentFolderPath = this.labelService.getUriLabel(resource); + } + + + a.innerText = name; + a.title = label; + a.setAttribute('aria-label', localize('welcomePage.openFolderWithPath', "Open folder {0} with path {1}", name, parentFolderPath)); + a.href = 'javascript:void(0)'; + a.addEventListener('click', e => { + /* __GDPR__ + "workbenchActionExecuted" : { + "id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + } + */ + this.telemetryService.publicLog('workbenchActionExecuted', { + id: 'openRecentFolder', + from: telemetryFrom + }); + this.windowService.openWindow([resource], { forceNewWindow: e.ctrlKey || e.metaKey }); + e.preventDefault(); + e.stopPropagation(); + }); + li.appendChild(a); + + const span = document.createElement('span'); + span.classList.add('path'); + span.classList.add('detail'); + span.innerText = parentFolderPath; + span.title = label; + li.appendChild(span); + + return li; + }); + } + private addExtensionList(container: HTMLElement, listSelector: string, suggestions: ExtensionSuggestion[], strings: Strings) { const list = container.querySelector(listSelector); if (list) { From 942cc332626e19ea6b4ecf68372b600cf6cdd1b5 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Jan 2019 11:17:59 +0100 Subject: [PATCH 0288/1066] explorer: focus tree when done editing fixes #66587 --- .../parts/files/electron-browser/views/explorerView.ts | 2 ++ 1 file changed, 2 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 e6f47540ace..3cfde92f9d5 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -178,6 +178,8 @@ export class ExplorerView extends ViewletPanel { if (isEditing) { DOM.addClass(this.tree.getHTMLElement(), 'highlight'); this.tree.reveal(e); + } else { + this.tree.domFocus(); } })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); From 05fc82c704fb5ed50776f752c1aa91910c064027 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 16 Jan 2019 11:12:45 +0100 Subject: [PATCH 0289/1066] #66397 Enablement service - take array of extensions to enable/disable - Event triggers array of changed extensions --- .../common/extensionEnablementService.ts | 24 ++- .../common/extensionManagement.ts | 4 +- .../extensionEnablementService.test.ts | 168 +++++++++--------- .../electron-browser/extensionsUtils.ts | 23 ++- .../node/extensionsWorkbenchService.ts | 60 ++++--- .../extensionsActions.test.ts | 40 ++--- .../electron-browser/extensionsViews.test.ts | 4 +- .../extensionsWorkbenchService.test.ts | 140 +++++++-------- .../page/electron-browser/welcomePage.ts | 11 +- .../electron-browser/extensionService.ts | 18 +- .../inactiveExtensionUrlHandler.ts | 2 +- 11 files changed, 251 insertions(+), 243 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts index 691bdc8c8b9..91bce7d47d9 100644 --- a/src/vs/platform/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/platform/extensionManagement/common/extensionEnablementService.ts @@ -21,8 +21,8 @@ export class ExtensionEnablementService extends Disposable implements IExtension _serviceBrand: any; - private _onEnablementChanged = new Emitter(); - public readonly onEnablementChanged: Event = this._onEnablementChanged.event; + private _onEnablementChanged = new Emitter(); + public readonly onEnablementChanged: Event = this._onEnablementChanged.event; private readonly storageManger: StorageManager; @@ -107,20 +107,29 @@ export class ExtensionEnablementService extends Disposable implements IExtension return true; } - setEnablement(extension: IExtension, newState: EnablementState): Promise { + async setEnablement(extensions: IExtension[], newState: EnablementState): Promise { const workspace = newState === EnablementState.WorkspaceDisabled || newState === EnablementState.WorkspaceEnabled; if (workspace && !this.hasWorkspace) { return Promise.reject(new Error(localize('noWorkspace', "No workspace."))); } + const result = await Promise.all(extensions.map(e => this._setEnablement(e, newState))); + const changedExtensions = extensions.filter((e, index) => result[index]); + if (changedExtensions.length) { + this._onEnablementChanged.fire(changedExtensions); + } + return result; + } + + private _setEnablement(extension: IExtension, newState: EnablementState): Promise { + const currentState = this._getEnablementState(extension.identifier); if (currentState === newState) { return Promise.resolve(false); } - switch (newState) { case EnablementState.Enabled: this._enableExtension(extension.identifier); @@ -136,7 +145,6 @@ export class ExtensionEnablementService extends Disposable implements IExtension break; } - this._onEnablementChanged.fire(extension); return Promise.resolve(true); } @@ -282,9 +290,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension private async onDidChangeStorage(extensionIdentifiers: IExtensionIdentifier[]): Promise { const installedExtensions = await this.extensionManagementService.getInstalled(); const extensions = installedExtensions.filter(installedExtension => extensionIdentifiers.some(identifier => areSameExtensions(identifier, installedExtension.identifier))); - for (const extension of extensions) { - this._onEnablementChanged.fire(extension); - } + this._onEnablementChanged.fire(extensions); } private _onDidInstallExtension(event: DidInstallExtensionEvent): void { @@ -292,7 +298,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension const wasDisabled = !this.isEnabled(event.local); this._reset(event.local.identifier); if (wasDisabled) { - this._onEnablementChanged.fire(event.local); + this._onEnablementChanged.fire([event.local]); } } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index d87647aaa88..5607fad1477 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -238,7 +238,7 @@ export interface IExtensionEnablementService { /** * Event to listen on for extension enablement changes */ - onEnablementChanged: Event; + onEnablementChanged: Event; /** * Returns all disabled extension identifiers for current workspace @@ -270,7 +270,7 @@ export interface IExtensionEnablementService { * * Throws error if enablement is requested for workspace and there is no workspace */ - setEnablement(extension: IExtension, state: EnablementState): Promise; + setEnablement(extensions: IExtension[], state: EnablementState): Promise; } export interface IExtensionsConfigContent { diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index d02b17a6687..c2078bdd87f 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -38,7 +38,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { } public async reset(): Promise { - return this.getDisabledExtensions().then(extensions => extensions.forEach(d => this.setEnablement(aLocalExtension(d.id), EnablementState.Enabled))); + return this.getDisabledExtensions().then(extensions => extensions.forEach(d => this.setEnablement([aLocalExtension(d.id)], EnablementState.Enabled))); } } @@ -65,7 +65,7 @@ suite('ExtensionEnablementService Test', () => { }); test('test when no extensions are disabled for workspace when there is no workspace', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY); return testObject.getDisabledExtensions().then(extensions => assert.deepEqual([], extensions)); @@ -73,20 +73,20 @@ suite('ExtensionEnablementService Test', () => { }); test('test disable an extension globally', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([{ id: 'pub.a' }], extensions)); }); test('test disable an extension globally should return truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(value => assert.ok(value)); }); test('test disable an extension globally triggers the change event', () => { const target = sinon.spy(); testObject.onEnablementChanged(target); - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => { assert.ok(target.calledOnce); assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); @@ -94,114 +94,114 @@ suite('ExtensionEnablementService Test', () => { }); test('test disable an extension globally again should return a falsy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(value => assert.ok(!value)); }); test('test state of globally disabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled)); }); test('test state of globally enabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled)); }); test('test disable an extension for workspace', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([{ id: 'pub.a' }], extensions)); }); test('test disable an extension for workspace returns a truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(value => assert.ok(value)); }); test('test disable an extension for workspace again should return a falsy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(value => assert.ok(!value)); }); test('test state of workspace disabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled)); }); test('test state of workspace and globally disabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled)); }); test('test state of workspace enabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled)); }); test('test state of globally disabled and workspace enabled extension', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceEnabled)); }); test('test state of an extension when disabled for workspace from workspace enabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.WorkspaceDisabled)); }); test('test state of an extension when disabled globally from workspace enabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled)); }); test('test state of an extension when disabled globally from workspace disabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Disabled)); }); test('test state of an extension when enabled globally from workspace enabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled)); }); test('test state of an extension when enabled globally from workspace disabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => assert.equal(testObject.getEnablementState(aLocalExtension('pub.a')), EnablementState.Enabled)); }); test('test disable an extension for workspace and then globally', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([{ id: 'pub.a' }], extensions)); }); test('test disable an extension for workspace and then globally return a truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(value => assert.ok(value)); }); test('test disable an extension for workspace and then globally trigger the change event', () => { const target = sinon.spy(); - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => { assert.ok(target.calledOnce); assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); @@ -209,23 +209,23 @@ suite('ExtensionEnablementService Test', () => { }); test('test disable an extension globally and then for workspace', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([{ id: 'pub.a' }], extensions)); }); test('test disable an extension globally and then for workspace return a truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(value => assert.ok(value)); }); test('test disable an extension globally and then for workspace triggers the change event', () => { const target = sinon.spy(); - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(() => { assert.ok(target.calledOnce); assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); @@ -234,28 +234,28 @@ suite('ExtensionEnablementService Test', () => { test('test disable an extension for workspace when there is no workspace throws error', () => { instantiationService.stub(IWorkspaceContextService, 'getWorkbenchState', WorkbenchState.EMPTY); - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => assert.fail('should throw an error'), error => assert.ok(error)); }); test('test enable an extension globally', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); test('test enable an extension globally return truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(value => assert.ok(value)); }); test('test enable an extension globally triggers change event', () => { const target = sinon.spy(); - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => testObject.onEnablementChanged(target)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => { assert.ok(target.calledOnce); assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); @@ -263,28 +263,28 @@ suite('ExtensionEnablementService Test', () => { }); test('test enable an extension globally when already enabled return falsy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled) .then(value => assert.ok(!value)); }); test('test enable an extension for workspace', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); test('test enable an extension for workspace return truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) .then(value => assert.ok(value)); }); test('test enable an extension for workspace triggers change event', () => { const target = sinon.spy(); - return testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.b')], EnablementState.WorkspaceDisabled) .then(() => testObject.onEnablementChanged(target)) - .then(() => testObject.setEnablement(aLocalExtension('pub.b'), EnablementState.WorkspaceEnabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.b')], EnablementState.WorkspaceEnabled)) .then(() => { assert.ok(target.calledOnce); assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.b' }); @@ -292,29 +292,29 @@ suite('ExtensionEnablementService Test', () => { }); test('test enable an extension for workspace when already enabled return truthy promise', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled) .then(value => assert.ok(value)); }); test('test enable an extension for workspace when disabled in workspace and gloablly', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceEnabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceEnabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); test('test enable an extension globally when disabled in workspace and gloablly', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Enabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); test('test installing an extension re-eanbles it when disabled globally', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.Disabled); + await testObject.setEnablement([local], EnablementState.Disabled); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([], extensions); @@ -322,7 +322,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not re-eanbles it when disabled globally', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.Disabled); + await testObject.setEnablement([local], EnablementState.Disabled); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([{ id: 'pub.a' }], extensions); @@ -330,9 +330,9 @@ suite('ExtensionEnablementService Test', () => { test('test installing an extension fires enablement change event when disabled globally', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.Disabled); + await testObject.setEnablement([local], EnablementState.Disabled); return new Promise((c, e) => { - testObject.onEnablementChanged(e => { + testObject.onEnablementChanged(([e]) => { if (e.identifier.id === local.identifier.id) { c(); } @@ -344,7 +344,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not fires enablement change event when disabled globally', async () => { const target = sinon.spy(); const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.Disabled); + await testObject.setEnablement([local], EnablementState.Disabled); testObject.onEnablementChanged(target); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); assert.ok(!target.called); @@ -352,7 +352,7 @@ suite('ExtensionEnablementService Test', () => { test('test installing an extension re-eanbles it when workspace disabled', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + await testObject.setEnablement([local], EnablementState.WorkspaceDisabled); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Install }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([], extensions); @@ -360,7 +360,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not re-eanbles it when workspace disabled', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + await testObject.setEnablement([local], EnablementState.WorkspaceDisabled); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); const extensions = await testObject.getDisabledExtensions(); assert.deepEqual([{ id: 'pub.a' }], extensions); @@ -368,9 +368,9 @@ suite('ExtensionEnablementService Test', () => { test('test installing an extension fires enablement change event when workspace disabled', async () => { const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + await testObject.setEnablement([local], EnablementState.WorkspaceDisabled); return new Promise((c, e) => { - testObject.onEnablementChanged(e => { + testObject.onEnablementChanged(([e]) => { if (e.identifier.id === local.identifier.id) { c(); } @@ -382,7 +382,7 @@ suite('ExtensionEnablementService Test', () => { test('test updating an extension does not fires enablement change event when workspace disabled', async () => { const target = sinon.spy(); const local = aLocalExtension('pub.a'); - await testObject.setEnablement(local, EnablementState.WorkspaceDisabled); + await testObject.setEnablement([local], EnablementState.WorkspaceDisabled); testObject.onEnablementChanged(target); didInstallEvent.fire({ local, identifier: local.identifier, operation: InstallOperation.Update }); assert.ok(!target.called); @@ -397,26 +397,26 @@ suite('ExtensionEnablementService Test', () => { }); test('test remove an extension from disablement list when uninstalled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => didUninstallEvent.fire({ identifier: { id: 'pub.a' } })) .then(() => testObject.getDisabledExtensions()) .then(extensions => assert.deepEqual([], extensions)); }); test('test isEnabled return false extension is disabled globally', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.Disabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a')))); }); test('test isEnabled return false extension is disabled in workspace', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => assert.ok(!testObject.isEnabled(aLocalExtension('pub.a')))); }); test('test isEnabled return true extension is not disabled', () => { - return testObject.setEnablement(aLocalExtension('pub.a'), EnablementState.WorkspaceDisabled) - .then(() => testObject.setEnablement(aLocalExtension('pub.c'), EnablementState.Disabled)) + return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) + .then(() => testObject.setEnablement([aLocalExtension('pub.c')], EnablementState.Disabled)) .then(() => assert.ok(testObject.isEnabled(aLocalExtension('pub.b')))); }); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts index bb382411969..92da0e2719c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsUtils.ts @@ -74,9 +74,7 @@ export class KeymapExtensions implements IWorkbenchContribution { */ this.telemetryService.publicLog('disableOtherKeymaps', telemetryData); if (confirmed) { - Promise.all(oldKeymaps.map(keymap => { - return this.extensionEnablementService.setEnablement(keymap.local, EnablementState.Disabled); - })); + this.extensionEnablementService.setEnablement(oldKeymaps.map(keymap => keymap.local), EnablementState.Disabled); } }; @@ -102,18 +100,19 @@ export function onExtensionChanged(accessor: ServicesAccessor): Event e.operation === InstallOperation.Install) .event; - return Event.debounce(Event.any( + return Event.debounce(Event.any( Event.chain(Event.any(onDidInstallExtension, extensionService.onDidUninstallExtension)) - .map(e => e.identifier) + .map(e => [e.identifier]) .event, - Event.map(extensionEnablementService.onEnablementChanged, e => e.identifier) - ), (list, id) => { - if (!list) { - return [id]; - } else if (list.some(l => !areSameExtensions(l, id))) { - list.push(id); + Event.map(extensionEnablementService.onEnablementChanged, extensions => extensions.map(e => e.identifier)) + ), (result: IExtensionIdentifier[] | undefined, identifiers: IExtensionIdentifier[]) => { + result = result || []; + for (const identifier of identifiers) { + if (result.some(l => !areSameExtensions(l, identifier))) { + result.push(identifier); + } } - return list; + return result; }); } diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 3fd9cd6f398..d3b7b2723d3 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -800,7 +800,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, } } } - return Promise.all(allExtensions.map(e => this.doSetEnablement(e, enablementState))); + return this.doSetEnablement(allExtensions, enablementState); } private getExtensionsRecursively(extensions: IExtension[], installed: IExtension[], enablementState: EnablementState, options: { dependencies: boolean, pack: boolean }, checked: IExtension[] = []): IExtension[] { @@ -872,28 +872,28 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, extension.displayName, dependents[0].displayName, dependents[1].displayName); } - private doSetEnablement(extension: IExtension, enablementState: EnablementState): Promise { - return this.extensionEnablementService.setEnablement(extension.local!, enablementState) - .then(changed => { - if (changed) { - /* __GDPR__ - "extension:enable" : { - "${include}": [ - "${GalleryExtensionTelemetryData}" - ] - } - */ - /* __GDPR__ - "extension:disable" : { - "${include}": [ - "${GalleryExtensionTelemetryData}" - ] - } - */ - this.telemetryService.publicLog(enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled ? 'extension:enable' : 'extension:disable', extension.telemetryData); + private async doSetEnablement(extensions: IExtension[], enablementState: EnablementState): Promise { + const changed = await this.extensionEnablementService.setEnablement(extensions.map(e => e.local!), enablementState); + for (let i = 0; i < changed.length; i++) { + if (changed[i]) { + /* __GDPR__ + "extension:enable" : { + "${include}": [ + "${GalleryExtensionTelemetryData}" + ] } - return changed; - }); + */ + /* __GDPR__ + "extension:disable" : { + "${include}": [ + "${GalleryExtensionTelemetryData}" + ] + } + */ + this.telemetryService.publicLog(enablementState === EnablementState.Enabled || enablementState === EnablementState.WorkspaceEnabled ? 'extension:enable' : 'extension:disable', extensions[i].telemetryData); + } + } + return changed; } get allowedBadgeProviders(): string[] { @@ -978,13 +978,15 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, this._onChange.fire(uninstalling); } - private onEnablementChanged({ identifier }: IPlatformExtension) { - const [extension] = this.local.filter(e => areSameExtensions(e.identifier, identifier)); - if (extension && extension.local) { - const enablementState = this.extensionEnablementService.getEnablementState(extension.local); - if (enablementState !== extension.enablementState) { - extension.enablementState = enablementState; - this._onChange.fire(extension); + private onEnablementChanged(platformExtensions: IPlatformExtension[]) { + const extensions = this.local.filter(e => platformExtensions.some(p => areSameExtensions(e.identifier, p.identifier))); + for (const extension of extensions) { + if (extension.local) { + const enablementState = this.extensionEnablementService.getEnablementState(extension.local); + if (enablementState !== extension.enablementState) { + extension.enablementState = enablementState; + this._onChange.fire(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 9e467e12658..bb2e6771815 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 @@ -589,7 +589,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableForWorkspaceAction when the extension is disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -604,7 +604,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableForWorkspaceAction when extension is disabled for workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -619,8 +619,8 @@ suite('ExtensionsActions Test', () => { test('Test EnableForWorkspaceAction when the extension is disabled globally and workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled)) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -653,7 +653,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableGloballyAction when the extension is disabled for workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -668,7 +668,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableGloballyAction when the extension is disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -683,8 +683,8 @@ suite('ExtensionsActions Test', () => { test('Test EnableGloballyAction when the extension is disabled in both', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled)) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -717,7 +717,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableDropDownAction when extension is installed and disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -732,7 +732,7 @@ suite('ExtensionsActions Test', () => { test('Test EnableDropDownAction when extension is installed and disabled for workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -793,7 +793,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableForWorkspaceAction when the extension is disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -808,7 +808,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableForWorkspaceAction when the extension is disabled workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -841,7 +841,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableGloballyAction when the extension is disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -856,7 +856,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableGloballyAction when the extension is disabled for workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -901,7 +901,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableDropDownAction when extension is installed and disabled globally', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -916,7 +916,7 @@ suite('ExtensionsActions Test', () => { test('Test DisableDropDownAction when extension is installed and disabled for workspace', () => { const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.WorkspaceDisabled) .then(() => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]); @@ -1185,7 +1185,7 @@ suite('ExtensionsActions Test', () => { test('Test ReloadAction when extension is updated when not running', () => { instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a', { version: '1.0.1' }); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); @@ -1240,7 +1240,7 @@ suite('ExtensionsActions Test', () => { test('Test ReloadAction when extension is enabled when not running', () => { instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); @@ -1261,7 +1261,7 @@ suite('ExtensionsActions Test', () => { test('Test ReloadAction when extension enablement is toggled when not running', () => { instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); @@ -1280,7 +1280,7 @@ suite('ExtensionsActions Test', () => { test('Test ReloadAction when extension is updated when not running and enabled', () => { instantiationService.stubPromise(IExtensionService, 'getExtensions', [{ identifier: new ExtensionIdentifier('pub.b'), extensionLocation: URI.file('pub.b') }]); const local = aLocalExtension('a', { version: '1.0.1' }); - return instantiationService.get(IExtensionEnablementService).setEnablement(local, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([local], EnablementState.Disabled) .then(() => { const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); instantiationService.get(IExtensionsWorkbenchService).onChange(() => testObject.update()); diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts index 3efccbcace4..72ca6a677ea 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsViews.test.ts @@ -132,8 +132,8 @@ suite('ExtensionsListView Tests', () => { ]); } }); - await (instantiationService.get(IExtensionEnablementService)).setEnablement(localDisabledTheme, EnablementState.Disabled); - await (instantiationService.get(IExtensionEnablementService)).setEnablement(localDisabledLanguage, EnablementState.Disabled); + await (instantiationService.get(IExtensionEnablementService)).setEnablement([localDisabledTheme], EnablementState.Disabled); + await (instantiationService.get(IExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.Disabled); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); testableView = instantiationService.createInstance(ExtensionsListView, {}); 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 7b7ec835cfb..38a85d5b399 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 @@ -713,8 +713,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test uninstalled extensions are always enabled', async () => { - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.WorkspaceDisabled)) .then(async () => { testObject = await aWorkbenchService(); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(aGalleryExtension('a'))); @@ -726,8 +726,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test enablement state installed enabled extension', async () => { - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); testObject = await aWorkbenchService(); @@ -740,10 +740,10 @@ suite('ExtensionsWorkbenchServiceTest', () => { test('test workspace disabled extension', async () => { const extensionA = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('d'), EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.WorkspaceDisabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('e'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('d')], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.WorkspaceDisabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('e')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA]); testObject = await aWorkbenchService(); @@ -756,9 +756,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { test('test globally disabled extension', async () => { const localExtension = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('d'), EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([localExtension], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('d')], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]); testObject = await aWorkbenchService(); @@ -770,8 +770,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test enablement state is updated for user extensions', async () => { - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); testObject = await aWorkbenchService(); @@ -785,7 +785,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { test('test enable extension globally when extension is disabled for workspace', async () => { const localExtension = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.WorkspaceDisabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([localExtension], EnablementState.WorkspaceDisabled) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]); testObject = await aWorkbenchService(); @@ -821,13 +821,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { test('test enablement state is updated on change from outside', async () => { const localExtension = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]); testObject = await aWorkbenchService(); - return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([localExtension], EnablementState.Disabled) .then(() => { const actual = testObject.local[0]; assert.equal(actual.enablementState, EnablementState.Disabled); @@ -840,9 +840,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -860,9 +860,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -880,9 +880,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -900,9 +900,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -915,9 +915,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -933,9 +933,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); const target = sinon.spy(); @@ -955,9 +955,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); const target = sinon.spy(); @@ -977,9 +977,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] }); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -996,9 +996,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] }); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -1015,9 +1015,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.a'] }); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -1032,9 +1032,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.b'] }); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); @@ -1050,9 +1050,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] }); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] }); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Enabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Enabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Enabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Enabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -1066,9 +1066,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); testObject = await aWorkbenchService(); @@ -1086,9 +1086,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Enabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Enabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); const target = sinon.spy(); @@ -1107,9 +1107,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b'); const extensionC = aLocalExtension('c'); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); const target = sinon.spy(); @@ -1129,9 +1129,9 @@ suite('ExtensionsWorkbenchServiceTest', () => { const extensionB = aLocalExtension('b', { extensionDependencies: ['pub.c'] }); const extensionC = aLocalExtension('c', { extensionDependencies: ['pub.a'] }); - return instantiationService.get(IExtensionEnablementService).setEnablement(extensionA, EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionB, EnablementState.Disabled)) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(extensionC, EnablementState.Disabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([extensionA], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionB], EnablementState.Disabled)) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([extensionC], EnablementState.Disabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extensionA, extensionB, extensionC]); @@ -1147,8 +1147,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); test('test change event is fired when disablement flags are changed', async () => { - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [aLocalExtension('a')]); testObject = await aWorkbenchService(); @@ -1162,15 +1162,15 @@ suite('ExtensionsWorkbenchServiceTest', () => { test('test change event is fired when disablement flags are changed from outside', async () => { const localExtension = aLocalExtension('a'); - return instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('c'), EnablementState.Disabled) - .then(() => instantiationService.get(IExtensionEnablementService).setEnablement(aLocalExtension('b'), EnablementState.WorkspaceDisabled)) + return instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('c')], EnablementState.Disabled) + .then(() => instantiationService.get(IExtensionEnablementService).setEnablement([aLocalExtension('b')], EnablementState.WorkspaceDisabled)) .then(async () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localExtension]); testObject = await aWorkbenchService(); const target = sinon.spy(); testObject.onChange(target); - return instantiationService.get(IExtensionEnablementService).setEnablement(localExtension, EnablementState.Disabled) + return instantiationService.get(IExtensionEnablementService).setEnablement([localExtension], EnablementState.Disabled) .then(() => assert.ok(target.calledOnce)); }); }); 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 9185347a099..14710340bde 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -482,7 +482,7 @@ class WelcomePage { .then(installed => { const local = installed.filter(i => areSameExtensions(extension.identifier, i.identifier))[0]; // TODO: Do this as part of the install to avoid multiple events. - return this.extensionEnablementService.setEnablement(local, EnablementState.Disabled).then(() => local); + return this.extensionEnablementService.setEnablement([local], EnablementState.Disabled).then(() => local); }); }); @@ -496,14 +496,13 @@ class WelcomePage { messageDelay.cancelAndSet(() => { this.notificationService.info(strings.installing.replace('{0}', extensionSuggestion.name)); }, 300); - Promise.all(extensionSuggestion.isKeymap ? extensions.filter(extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled) - .map(extension => { - return this.extensionEnablementService.setEnablement(extension.local, EnablementState.Disabled); - }) : []).then(() => { + const extensionsToDisable = extensions.filter(extension => isKeymapExtension(this.tipsService, extension) && extension.globallyEnabled).map(extension => extension.local); + extensionsToDisable.length ? this.extensionEnablementService.setEnablement(extensionsToDisable, EnablementState.Disabled) : Promise.resolve() + .then(() => { return foundAndInstalled.then(foundExtension => { messageDelay.cancel(); if (foundExtension) { - return this.extensionEnablementService.setEnablement(foundExtension, EnablementState.Enabled) + return this.extensionEnablementService.setEnablement([foundExtension], EnablementState.Enabled) .then(() => { /* __GDPR__FRAGMENT__ "WelcomePageInstalled-2" : { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index a3fb979f728..dd47be70fc5 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -114,13 +114,15 @@ export class ExtensionService extends Disposable implements IExtensionService { }]); } - this._extensionEnablementService.onEnablementChanged((extension) => { - if (this._extensionEnablementService.isEnabled(extension)) { - // an extension has been enabled - this._addExtension(extension); - } else { - // an extension has been disabled - this._removeExtension(extension.identifier.id); + this._extensionEnablementService.onEnablementChanged((extensions) => { + for (const extension of extensions) { + if (this._extensionEnablementService.isEnabled(extension)) { + // an extension has been enabled + this._addExtension(extension); + } else { + // an extension has been disabled + this._removeExtension(extension.identifier.id); + } } }); @@ -599,7 +601,7 @@ export class ExtensionService extends Disposable implements IExtensionService { }); if (extensionsToDisable.length) { - return Promise.all(extensionsToDisable.map(e => this._extensionEnablementService.setEnablement(toExtension(e), EnablementState.Disabled))) + return this._extensionEnablementService.setEnablement(extensionsToDisable.map(e => toExtension(e)), EnablementState.Disabled) .then(() => runtimeExtensions); } else { return runtimeExtensions; diff --git a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts index 08710361143..0b63147da65 100644 --- a/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts @@ -183,7 +183,7 @@ export class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler { return; } - await this.extensionEnablementService.setEnablement(extension, EnablementState.Enabled); + await this.extensionEnablementService.setEnablement([extension], EnablementState.Enabled); await this.reloadAndHandle(uri); } } From edf468ac0499d1c95ab3c66c2565c923f8ec623b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 16 Jan 2019 11:18:29 +0100 Subject: [PATCH 0290/1066] Fix tests --- .../extensionEnablementService.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index c2078bdd87f..65c7ea1b099 100644 --- a/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -89,14 +89,14 @@ suite('ExtensionEnablementService Test', () => { return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => { assert.ok(target.calledOnce); - assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + assert.deepEqual((target.args[0][0][0]).identifier, { id: 'pub.a' }); }); }); test('test disable an extension globally again should return a falsy promise', () => { return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled) .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) - .then(value => assert.ok(!value)); + .then(value => assert.ok(!value[0])); }); test('test state of globally disabled extension', () => { @@ -124,7 +124,7 @@ suite('ExtensionEnablementService Test', () => { test('test disable an extension for workspace again should return a falsy promise', () => { return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled) .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) - .then(value => assert.ok(!value)); + .then(value => assert.ok(!value[0])); }); test('test state of workspace disabled extension', () => { @@ -204,7 +204,7 @@ suite('ExtensionEnablementService Test', () => { .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Disabled)) .then(() => { assert.ok(target.calledOnce); - assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + assert.deepEqual((target.args[0][0][0]).identifier, { id: 'pub.a' }); }); }); @@ -228,7 +228,7 @@ suite('ExtensionEnablementService Test', () => { .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.WorkspaceDisabled)) .then(() => { assert.ok(target.calledOnce); - assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + assert.deepEqual((target.args[0][0][0]).identifier, { id: 'pub.a' }); }); }); @@ -258,13 +258,13 @@ suite('ExtensionEnablementService Test', () => { .then(() => testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled)) .then(() => { assert.ok(target.calledOnce); - assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.a' }); + assert.deepEqual((target.args[0][0][0]).identifier, { id: 'pub.a' }); }); }); test('test enable an extension globally when already enabled return falsy promise', () => { return testObject.setEnablement([aLocalExtension('pub.a')], EnablementState.Enabled) - .then(value => assert.ok(!value)); + .then(value => assert.ok(!value[0])); }); test('test enable an extension for workspace', () => { @@ -287,7 +287,7 @@ suite('ExtensionEnablementService Test', () => { .then(() => testObject.setEnablement([aLocalExtension('pub.b')], EnablementState.WorkspaceEnabled)) .then(() => { assert.ok(target.calledOnce); - assert.deepEqual((target.args[0][0]).identifier, { id: 'pub.b' }); + assert.deepEqual((target.args[0][0][0]).identifier, { id: 'pub.b' }); }); }); From bc6bc84ec728db52baf058b14d8b43f1fa4321ee Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Jan 2019 11:27:43 +0100 Subject: [PATCH 0291/1066] fixes #66566 --- .../parts/files/electron-browser/views/explorerView.ts | 8 ++++++-- 1 file changed, 6 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 3cfde92f9d5..afb0bad539d 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -293,8 +293,12 @@ export class ExplorerView extends ViewletPanel { const selection = e.elements; // Do not react if the user is expanding selection if (selection && selection.length === 1) { - if (selection[0].isDirectory || !selection[0].name) { - // Do not react if user is clicking on directories or explorer items which are input placeholders + if (!selection[0].name) { + // Do not react if user is clicking on explorer items which are input placeholders + return; + } + if (selection[0].isDirectory) { + this.tree.toggleCollapsed(selection[0]); return; } let isDoubleClick = false; From b410581538c2568fc9c3d09248521a0471e9b96b Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Wed, 16 Jan 2019 11:44:28 +0100 Subject: [PATCH 0292/1066] Fixes #66548: Avoid double async-calls --- src/main.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main.js b/src/main.js index 6b2bfae32c6..7429fcc087a 100644 --- a/src/main.js +++ b/src/main.js @@ -434,20 +434,16 @@ function getUserDefinedLocale() { } const localeConfig = path.join(userDataPath, 'User', 'locale.json'); - return exists(localeConfig).then((result) => { - if (result) { - return bootstrap.readFile(localeConfig).then((content) => { - content = stripComments(content); - try { - const value = JSON.parse(content).locale; - return value && typeof value === 'string' ? value.toLowerCase() : undefined; - } catch (e) { - return undefined; - } - }); - } else { + return bootstrap.readFile(localeConfig).then((content) => { + content = stripComments(content); + try { + const value = JSON.parse(content).locale; + return value && typeof value === 'string' ? value.toLowerCase() : undefined; + } catch (e) { return undefined; } + }, () => { + return undefined; }); } From 217824cc163ba532cc0b55f22987daaef1a7b7c9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 16 Jan 2019 12:24:36 +0100 Subject: [PATCH 0293/1066] Simplify transformers --- src/vs/base/common/uriIpc.ts | 10 ++++++---- src/vs/code/node/cliProcessMain.ts | 2 +- src/vs/platform/download/node/downloadIpc.ts | 2 +- .../extensionManagement/node/extensionManagementIpc.ts | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/base/common/uriIpc.ts b/src/vs/base/common/uriIpc.ts index 10e24307da0..4cee99c11f7 100644 --- a/src/vs/base/common/uriIpc.ts +++ b/src/vs/base/common/uriIpc.ts @@ -8,8 +8,8 @@ import { MarshalledObject } from 'vs/base/common/marshalling'; export interface IURITransformer { transformIncoming(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI): URI; transformOutgoing(uri: UriComponents): UriComponents; + transformOutgoingURI(uri: URI): URI; } export const DefaultURITransformer: IURITransformer = new class { @@ -17,9 +17,11 @@ export const DefaultURITransformer: IURITransformer = new class { return uri; } - transformOutgoing(uri: URI): URI; - transformOutgoing(uri: UriComponents): UriComponents; - transformOutgoing(uri: URI | UriComponents): URI | UriComponents { + transformOutgoing(uri: UriComponents): UriComponents { + return uri; + } + + transformOutgoingURI(uri: URI): URI { return uri; } }; diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 2bc020d9a28..50b202a6a64 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -63,7 +63,7 @@ export function getIdAndVersion(id: string): [string, string | undefined] { } -class Main { +export class Main { constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts index 03369f885ca..10210cfb5b0 100644 --- a/src/vs/platform/download/node/downloadIpc.ts +++ b/src/vs/platform/download/node/downloadIpc.ts @@ -47,7 +47,7 @@ export class DownloadServiceChannelClient implements IDownloadService { constructor(private channel: IChannel, private getUriTransformer: () => IURITransformer) { } download(from: URI, to: string): Promise { - from = this.getUriTransformer().transformOutgoing(from); + from = this.getUriTransformer().transformOutgoingURI(from); const dirName = path.dirname(to); let out: fs.WriteStream; return new Promise((c, e) => { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts index 56e64302099..e7fafc49e92 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementIpc.ts @@ -16,7 +16,7 @@ function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | } function transformOutgoingURI(uri: URI, transformer: IURITransformer | null): URI { - return transformer ? transformer.transformOutgoing(uri) : uri; + return transformer ? transformer.transformOutgoingURI(uri) : uri; } function transformIncomingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { @@ -27,7 +27,7 @@ function transformIncomingExtension(extension: ILocalExtension, transformer: IUR } function transformOutgoingExtension(extension: ILocalExtension, transformer: IURITransformer | null): ILocalExtension { - return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoing(value) : undefined) : extension; + return transformer ? cloneAndChange(extension, value => value instanceof URI ? transformer.transformOutgoingURI(value) : undefined) : extension; } export class ExtensionManagementChannel implements IServerChannel { From 19adeb1620afad5354734c81612b28bdf0a81ab8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 16 Jan 2019 12:27:35 +0100 Subject: [PATCH 0294/1066] Simplify extHostSearch --- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHostSearch.ts | 22 ++++++------------- .../api/extHostSearch.test.ts | 14 +----------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index f044aa65682..efaabac633b 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -115,7 +115,7 @@ export function createApiFactory( const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); - const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService, extHostConfiguration)); + const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService)); const extHostTask = rpcProtocol.set(ExtHostContext.ExtHostTask, new ExtHostTask(rpcProtocol, extHostWorkspace, extHostDocumentsAndEditors, extHostConfiguration)); const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 47ab14cf1a3..53ff6f41386 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -9,7 +9,6 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; import { ILogService } from 'vs/platform/log/common/log'; import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery } from 'vs/platform/search/common/search'; -import { ExtHostConfiguration, ExtHostConfigProvider } from 'vs/workbench/api/node/extHostConfiguration'; import { FileIndexSearchManager } from 'vs/workbench/api/node/extHostSearch.fileIndex'; import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager'; import { SearchService } from 'vs/workbench/services/search/node/rawSearchService'; @@ -41,14 +40,10 @@ export class ExtHostSearch implements ExtHostSearchShape { private _fileSearchManager: FileSearchManager; private _fileIndexSearchManager: FileIndexSearchManager; - constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _logService: ILogService, configService: ExtHostConfiguration, private _extfs = extfs) { + constructor(mainContext: IMainContext, private _schemeTransformer: ISchemeTransformer, private _logService: ILogService, private _extfs = extfs) { this._proxy = mainContext.getProxy(MainContext.MainThreadSearch); this._fileSearchManager = new FileSearchManager(); this._fileIndexSearchManager = new FileIndexSearchManager(); - - configService.getConfigProvider().then((configProvider) => { - registerEHProviders(this, _logService, configProvider); - }); } private _transformScheme(scheme: string): string { @@ -60,7 +55,7 @@ export class ExtHostSearch implements ExtHostSearchShape { registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable { if (this._textSearchUsedSchemes.has(scheme)) { - throw new Error(`a provider for the scheme '${scheme}' is already registered`); + throw new Error(`a text search provider for the scheme '${scheme}' is already registered`); } this._textSearchUsedSchemes.add(scheme); @@ -76,7 +71,7 @@ export class ExtHostSearch implements ExtHostSearchShape { registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable { if (this._fileSearchUsedSchemes.has(scheme)) { - throw new Error(`a provider for the scheme '${scheme}' is already registered`); + throw new Error(`a file search provider for the scheme '${scheme}' is already registered`); } this._fileSearchUsedSchemes.add(scheme); @@ -178,13 +173,10 @@ export class ExtHostSearch implements ExtHostSearchShape { } } -function registerEHProviders(extHostSearch: ExtHostSearch, logService: ILogService, configService: ExtHostConfigProvider) { - if (configService.getConfiguration('searchRipgrep').enable || configService.getConfiguration('search').runInExtensionHost) { - const outputChannel = new OutputChannel(logService); - extHostSearch.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel)); - - extHostSearch.registerInternalFileSearchProvider('file', new SearchService()); - } +export function registerEHSearchProviders(extHostSearch: ExtHostSearch, logService: ILogService): void { + const outputChannel = new OutputChannel(logService); + extHostSearch.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel)); + extHostSearch.registerInternalFileSearchProvider('file', new SearchService()); } function reviveQuery(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery { diff --git a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts index b5ca6df3e9f..985ccdb5ab4 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostSearch.test.ts @@ -12,7 +12,6 @@ import { URI, UriComponents } from 'vs/base/common/uri'; import * as extfs from 'vs/base/node/extfs'; import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/platform/search/common/search'; import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/node/extHost.protocol'; -import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; import { Range } from 'vs/workbench/api/node/extHostTypes'; import { extensionResultIsMatch } from 'vs/workbench/services/search/node/textSearchManager'; @@ -60,16 +59,6 @@ class MockMainThreadSearch implements MainThreadSearchShape { } } -class MockExtHostConfiguration { - getConfigProvider(): Promise { - return Promise.resolve({ - getConfiguration: (section?: string, resource?: URI, extensionId?: string): vscode.WorkspaceConfiguration => { - return {}; - } - }); - } -} - let mockExtfs: Partial; suite('ExtHostSearch', () => { @@ -142,12 +131,11 @@ suite('ExtHostSearch', () => { mockMainThreadSearch = new MockMainThreadSearch(); const logService = new TestLogService(); - const ehConfiguration: ExtHostConfiguration = new MockExtHostConfiguration() as any; rpcProtocol.set(MainContext.MainThreadSearch, mockMainThreadSearch); mockExtfs = {}; - extHostSearch = new ExtHostSearch(rpcProtocol, null!, logService, ehConfiguration, mockExtfs as typeof extfs); + extHostSearch = new ExtHostSearch(rpcProtocol, null!, logService, mockExtfs as typeof extfs); }); teardown(() => { From 20b2799124d987499eca20532a9e77eda1e368d0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 16 Jan 2019 12:46:08 +0100 Subject: [PATCH 0295/1066] tree: select all should work in hierarchies related to #63717 --- src/vs/workbench/electron-browser/commands.ts | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 6c97ce6a45e..2d93dad4922 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -594,23 +594,60 @@ export function registerCommands(): void { // Trees else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { const tree = focused; - const selection: any[] = []; + 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) => { - node.children.forEach(child => { + for (const child of node.children) { if (child.visible) { - selection.push(child.element); + newSelection.push(child.element); if (!child.collapsed) { visit(child); } } - }); + } }; - visit(tree.getNode()); + // Add the whole scope subtree to the new selection + visit(tree.getNode(scope)); const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setSelection(selection, fakeKeyboardEvent); + tree.setSelection(newSelection, fakeKeyboardEvent); } } }); From 6f3422a0d8ed9e4330ecbbea321d54391d27e4f0 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Tue, 15 Jan 2019 17:25:40 +0100 Subject: [PATCH 0296/1066] preserve type of file --- .../parts/debug/common/debugUtils.ts | 17 ++++- .../debug/electron-browser/rawDebugSession.ts | 64 ++++++++++++------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index 89ed9de8ed8..a1effe8f738 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -134,6 +134,11 @@ export function convertToVSCPaths(message: DebugProtocol.ProtocolMessage, fixSou } function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: boolean, source: DebugProtocol.Source | undefined) => void): void { + + const tmpSource: DebugProtocol.Source = { + path: '' + }; + switch (msg.type) { case 'event': const event = msg; @@ -164,9 +169,15 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: fixSourcePaths(true, (request.arguments).source); break; case 'launchVSCode': - const source = { path: request.arguments.extensionDevelopmentPath }; - fixSourcePaths(false, source); - request.arguments.extensionDevelopmentPath = source.path; + tmpSource.path = request.arguments.extensionDevelopmentPath; + fixSourcePaths(false, tmpSource); + request.arguments.extensionDevelopmentPath = tmpSource.path; + + request.arguments.pathArgs.forEach(p => { + tmpSource.path = p.path; + fixSourcePaths(false, tmpSource); + p.path = tmpSource.path; + }); break; default: break; diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 54b1b4c8113..58d8c7096fb 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -17,13 +17,15 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' export interface ILaunchVSCodeArguments { sessionId: string; - debugMode: 'noDebug' | 'break'; + debugMode: 'noDebug' | 'break' | 'noBreak'; debugPort: number; - extensionDevelopmentPath: string; options: string[]; - args: string[]; - cwd?: string; - env: { [key: string]: string | null; }; + extensionDevelopmentPath: string; + pathArgs: { + type: 'folder' | 'file'; + path: string; + }[]; + env?: { [key: string]: string | null; }; } /** @@ -539,26 +541,30 @@ export class RawDebugSession { private launch(VSCodeArgs: ILaunchVSCodeArguments): Promise { - let envArgs = process.env; - if (VSCodeArgs.env) { - // merge environment variables into a copy of the process.env - envArgs = objects.mixin(objects.mixin({}, process.env), VSCodeArgs.env); - // and delete some if necessary - Object.keys(envArgs).filter(k => envArgs[k] === null).forEach(key => delete envArgs[key]); - } const spawnOpts: cp.SpawnOptions = { - env: envArgs, detached: false // https://github.com/Microsoft/vscode/issues/57018 }; - if (VSCodeArgs.cwd) { - spawnOpts.cwd = VSCodeArgs.cwd; + + if (VSCodeArgs.env) { + // merge environment variables into a copy of the process.env + const envArgs = objects.mixin(objects.mixin({}, process.env), VSCodeArgs.env); + // and delete some if necessary + Object.keys(envArgs).filter(k => envArgs[k] === null).forEach(key => delete envArgs[key]); + spawnOpts.env = envArgs; } let spawnArgs = [].concat(VSCodeArgs.options); - if (VSCodeArgs.debugMode !== 'noDebug') { - spawnArgs.push(`--debugBrkPluginHost=${VSCodeArgs.debugPort}`); + switch (VSCodeArgs.debugMode) { + case 'noDebug': + break; + case 'break': + spawnArgs.push(`--inspect-brk-extensions=${VSCodeArgs.debugPort}`); + break; + case 'noBreak': + spawnArgs.push(`--inspect-extensions=${VSCodeArgs.debugPort}`); + break; } // pass the debug session ID to the EH so that broadcast events know where they come from @@ -575,19 +581,31 @@ export class RawDebugSession { return Promise.reject(new Error(`VS Code executable unknown`)); } - // if VS Code runs out of sources, add the path to the VS Code workspace as a first argument so that Electron turns into VS Code + // if VS Code runs out of sources, add the VS Code workspace path as the first argument so that Electron turns into VS Code const electronIdx = runtimeExecutable.indexOf(process.platform === 'win32' ? '\\.build\\electron\\' : '/.build/electron/'); - if (electronIdx > 0 && VSCodeArgs.args.length > 0) { - // guess the VS Code workspace path + if (electronIdx > 0) { + // guess the VS Code workspace path from the executable const vscodeWorkspacePath = runtimeExecutable.substr(0, electronIdx); - // only add path if user hasn't already added path - if (VSCodeArgs.args.length === 0 || VSCodeArgs.args[0].indexOf(vscodeWorkspacePath) !== 0) { + // only add path if user hasn't already added that path + if (VSCodeArgs.pathArgs.length === 0 || VSCodeArgs.pathArgs[0].path.indexOf(vscodeWorkspacePath) !== 0) { spawnArgs.push(vscodeWorkspacePath); } } - spawnArgs = spawnArgs.concat(VSCodeArgs.args); + VSCodeArgs.pathArgs.forEach(p => { + switch (p.type) { + case 'file': + spawnArgs.push(`--file-uri=${p.path}`); + break; + case 'folder': + spawnArgs.push(`--folder-uri=${p.path}`); + break; + default: + spawnArgs.push(p.path); + break; + } + }); // Workaround for bug Microsoft/vscode#45832 if (process.platform === 'win32' && runtimeExecutable.indexOf(' ') > 0) { From 61149bb090fbef2df750cbf741836c3870d877ec Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Jan 2019 13:02:28 +0100 Subject: [PATCH 0297/1066] explorer: only toggle collapsed directory on keyboard selection event --- .../parts/files/electron-browser/views/explorerView.ts | 4 +++- 1 file changed, 3 insertions(+), 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 afb0bad539d..9729d0ae69f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -298,7 +298,9 @@ export class ExplorerView extends ViewletPanel { return; } if (selection[0].isDirectory) { - this.tree.toggleCollapsed(selection[0]); + if (e.browserEvent instanceof KeyboardEvent) { + this.tree.toggleCollapsed(selection[0]); + } return; } let isDoubleClick = false; From 905f99525dcada38419a7495750e682583f9b219 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Jan 2019 13:03:02 +0100 Subject: [PATCH 0298/1066] open editors: use list dnd not, not a custom solution --- .../electron-browser/views/openEditorsView.ts | 176 ++++++------------ 1 file changed, 55 insertions(+), 121 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 19c4da353c6..be88f381cb9 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -24,7 +24,7 @@ import { attachStylerCallback } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { ResourceLabels, IResourceLabel, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -34,9 +34,12 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { DirtyEditorContext, OpenEditorsGroupContext } from 'vs/workbench/parts/files/electron-browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { fillResourceDataTransfers, ResourcesDropHandler, LocalSelectionTransfer, CodeDataTransfers } from 'vs/workbench/browser/dnd'; +import { ResourcesDropHandler } from 'vs/workbench/browser/dnd'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { memoize } from 'vs/base/common/decorators'; +import { DesktopDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; const $ = dom.$; @@ -200,15 +203,6 @@ export class OpenEditorsView extends ViewletPanel { dom.addClass(container, 'show-file-icons'); const delegate = new OpenEditorsDelegate(); - const getSelectedElements = () => { - const selected = this.list.getSelectedElements(); - const focused = this.list.getFocusedElements(); - if (focused.length && selected.indexOf(focused[0]) >= 0) { - return selected; - } - - return focused; - }; if (this.list) { this.list.dispose(); @@ -218,10 +212,11 @@ export class OpenEditorsView extends ViewletPanel { } this.listLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility } as IResourceLabelsContainer); this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [ - new EditorGroupRenderer(this.keybindingService, this.instantiationService, this.editorGroupService), - new OpenEditorRenderer(this.listLabels, getSelectedElements, this.instantiationService, this.keybindingService, this.configurationService, this.editorGroupService) + new EditorGroupRenderer(this.keybindingService, this.instantiationService), + new OpenEditorRenderer(this.listLabels, this.instantiationService, this.keybindingService, this.configurationService) ], { - identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() } + identityProvider: { getId: (element: OpenEditor | IEditorGroup) => element instanceof OpenEditor ? element.getId() : element.id.toString() }, + dnd: new OpenEditorsDragAndDrop(this.instantiationService, this.editorGroupService) }) as WorkbenchList; this.disposables.push(this.list); this.disposables.push(this.listLabels); @@ -476,8 +471,6 @@ interface IOpenEditorTemplateData { root: IResourceLabel; actionBar: ActionBar; actionRunner: OpenEditorActionRunner; - openEditor: OpenEditor; - toDispose: IDisposable[]; } interface IEditorGroupTemplateData { @@ -485,7 +478,6 @@ interface IEditorGroupTemplateData { name: HTMLSpanElement; actionBar: ActionBar; editorGroup: IEditorGroup; - toDispose: IDisposable[]; } class OpenEditorActionRunner extends ActionRunner { @@ -513,41 +505,12 @@ class OpenEditorsDelegate implements IListVirtualDelegate { static readonly ID = 'editorgroup'; - private transfer = LocalSelectionTransfer.getInstance(); - constructor( private keybindingService: IKeybindingService, private instantiationService: IInstantiationService, - private editorGroupService: IEditorGroupsService ) { // noop } @@ -570,29 +533,6 @@ class EditorGroupRenderer implements IListRenderer { - if (dropOnEditorSupported(e)) { - dom.addClass(container, 'focused'); - } - })); - editorGroupTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_LEAVE, () => { - dom.removeClass(container, 'focused'); - })); - editorGroupTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DROP, e => { - dom.removeClass(container, 'focused'); - - - if (this.transfer.hasData(OpenEditor.prototype)) { - this.transfer.getData(OpenEditor.prototype).forEach(oe => - oe.group.moveEditor(oe.editor, editorGroupTemplate.editorGroup, { preserveFocus: true })); - this.editorGroupService.activateGroup(editorGroupTemplate.editorGroup); - } else { - const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false }); - dropHandler.handleDrop(e, () => editorGroupTemplate.editorGroup, () => editorGroupTemplate.editorGroup.focus()); - } - })); - return editorGroupTemplate; } @@ -604,22 +544,17 @@ class EditorGroupRenderer implements IListRenderer { static readonly ID = 'openeditor'; - private transfer = LocalSelectionTransfer.getInstance(); - constructor( private labels: ResourceLabels, - private getSelectedElements: () => Array, private instantiationService: IInstantiationService, private keybindingService: IKeybindingService, - private configurationService: IConfigurationService, - private editorGroupService: IEditorGroupsService + private configurationService: IConfigurationService ) { // noop } @@ -641,55 +576,10 @@ class OpenEditorRenderer implements IListRenderer { - const dragged = this.getSelectedElements().filter(e => e instanceof OpenEditor && !!e.getResource()); - - const dragImage = document.createElement('div'); - e.dataTransfer.effectAllowed = 'copyMove'; - dragImage.className = 'monaco-tree-drag-image'; - dragImage.textContent = dragged.length === 1 ? editorTemplate.openEditor.editor.getName() : String(dragged.length); - document.body.appendChild(dragImage); - e.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => document.body.removeChild(dragImage), 0); - - this.transfer.setData(dragged, OpenEditor.prototype); - - if (editorTemplate.openEditor && editorTemplate.openEditor.editor) { - this.instantiationService.invokeFunction(fillResourceDataTransfers, dragged.map(d => d.getResource()), e); - } - })); - editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_OVER, (e: DragEvent) => { - if (dropOnEditorSupported(e)) { - dom.addClass(container, 'focused'); - } - })); - editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_LEAVE, () => { - dom.removeClass(container, 'focused'); - })); - editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DROP, (e: DragEvent) => { - dom.removeClass(container, 'focused'); - const index = editorTemplate.openEditor.group.getIndexOfEditor(editorTemplate.openEditor.editor); - - if (this.transfer.hasData(OpenEditor.prototype)) { - this.transfer.getData(OpenEditor.prototype).forEach((oe, offset) => - oe.group.moveEditor(oe.editor, editorTemplate.openEditor.group, { index: index + offset, preserveFocus: true })); - this.editorGroupService.activateGroup(editorTemplate.openEditor.group); - } else { - const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false }); - dropHandler.handleDrop(e, () => editorTemplate.openEditor.group, () => editorTemplate.openEditor.group.focus(), index); - } - })); - editorTemplate.toDispose.push(dom.addDisposableListener(container, dom.EventType.DRAG_END, () => { - this.transfer.clearData(OpenEditor.prototype); - })); - return editorTemplate; } renderElement(editor: OpenEditor, index: number, templateData: IOpenEditorTemplateData): void { - templateData.openEditor = editor; templateData.actionRunner.editor = editor; editor.isDirty() ? dom.addClass(templateData.container, 'dirty') : dom.removeClass(templateData.container, 'dirty'); templateData.root.setEditor(editor.editor, { @@ -703,6 +593,50 @@ class OpenEditorRenderer implements IListRenderer { + + constructor( + private instantiationService: IInstantiationService, + private editorGroupService: IEditorGroupsService + ) { } + + @memoize private get dropHandler(): ResourcesDropHandler { + return this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false }); + } + + getDragURI(element: OpenEditor | IEditorGroup): string | null { + return element instanceof OpenEditor ? element.getResource().toString() : null; + } + + getDragLabel?(elements: (OpenEditor | IEditorGroup)[]): string { + if (elements.length > 1) { + return String(elements.length); + } + const element = elements[0]; + + return element instanceof OpenEditor ? element.editor.getName() : element.label; + } + + onDragOver(data: IDragAndDropData, targetElement: OpenEditor | IEditorGroup, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { + return true; + } + + drop(data: IDragAndDropData, targetElement: OpenEditor | IEditorGroup, targetIndex: number, originalEvent: DragEvent): void { + const group = targetElement instanceof OpenEditor ? targetElement.group : targetElement; + const index = targetElement instanceof OpenEditor ? targetElement.group.getIndexOfEditor(targetElement.editor) : 0; + + if (data instanceof DesktopDragAndDropData) { + this.dropHandler.handleDrop(originalEvent, () => group, () => group.focus(), index); + } else { + const elementsData = (data as ElementsDragAndDropData).elements; + elementsData.forEach((oe, offset) => { + oe.group.moveEditor(oe.editor, group, { index: index + offset, preserveFocus: true }); + }); + this.editorGroupService.activateGroup(group); + } + } } From c42b7620ff43f3bbbe93a719af4201a876cf717f Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Wed, 16 Jan 2019 14:02:52 +0100 Subject: [PATCH 0299/1066] Strict null --- .../parts/welcome/page/electron-browser/welcomePage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 14710340bde..a4fa09d9c34 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -312,7 +312,7 @@ class WelcomePage { if (!ul) { return; } - const moreRecent = ul.querySelector('.moreRecent'); + const moreRecent = ul.querySelector('.moreRecent')!; const workspacesToShow = workspaces.slice(0, 5); const updateEntries = () => { const listEntries = this.createListEntries(workspacesToShow); From 5bb29d7b07799022571a4a24242ac862ade87673 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Wed, 16 Jan 2019 15:17:40 +0100 Subject: [PATCH 0300/1066] return pid from vscodeLaunch --- .../debug/electron-browser/rawDebugSession.ts | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 58d8c7096fb..bdd0199a367 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -494,7 +494,10 @@ export class RawDebugSession { switch (request.command) { case 'launchVSCode': - this.launch(request.arguments).then(_ => { + this.launchVsCode(request.arguments).then(pid => { + response.body = { + processId: pid + }; safeSendResponse(response); }, err => { response.success = false; @@ -539,41 +542,40 @@ export class RawDebugSession { } } - private launch(VSCodeArgs: ILaunchVSCodeArguments): Promise { - + private launchVsCode(args: ILaunchVSCodeArguments): Promise { const spawnOpts: cp.SpawnOptions = { detached: false // https://github.com/Microsoft/vscode/issues/57018 }; - if (VSCodeArgs.env) { + if (args.env) { // merge environment variables into a copy of the process.env - const envArgs = objects.mixin(objects.mixin({}, process.env), VSCodeArgs.env); + const envArgs = objects.mixin(objects.mixin({}, process.env), args.env); // and delete some if necessary Object.keys(envArgs).filter(k => envArgs[k] === null).forEach(key => delete envArgs[key]); spawnOpts.env = envArgs; } - let spawnArgs = [].concat(VSCodeArgs.options); + let spawnArgs = [].concat(args.options); - switch (VSCodeArgs.debugMode) { + switch (args.debugMode) { case 'noDebug': break; case 'break': - spawnArgs.push(`--inspect-brk-extensions=${VSCodeArgs.debugPort}`); + spawnArgs.push(`--inspect-brk-extensions=${args.debugPort}`); break; case 'noBreak': - spawnArgs.push(`--inspect-extensions=${VSCodeArgs.debugPort}`); + spawnArgs.push(`--inspect-extensions=${args.debugPort}`); break; } // pass the debug session ID to the EH so that broadcast events know where they come from - if (VSCodeArgs.sessionId) { - spawnArgs.push(`--debugId=${VSCodeArgs.sessionId}`); + if (args.sessionId) { + spawnArgs.push(`--debugId=${args.sessionId}`); } - if (VSCodeArgs.extensionDevelopmentPath) { - spawnArgs.push(`--extensionDevelopmentPath=${VSCodeArgs.extensionDevelopmentPath}`); + if (args.extensionDevelopmentPath) { + spawnArgs.push(`--extensionDevelopmentPath=${args.extensionDevelopmentPath}`); } let runtimeExecutable = this.environmentService['execPath']; @@ -588,12 +590,12 @@ export class RawDebugSession { const vscodeWorkspacePath = runtimeExecutable.substr(0, electronIdx); // only add path if user hasn't already added that path - if (VSCodeArgs.pathArgs.length === 0 || VSCodeArgs.pathArgs[0].path.indexOf(vscodeWorkspacePath) !== 0) { + if (args.pathArgs.length === 0 || args.pathArgs[0].path.indexOf(vscodeWorkspacePath) !== 0) { spawnArgs.push(vscodeWorkspacePath); } } - VSCodeArgs.pathArgs.forEach(p => { + args.pathArgs.forEach(p => { switch (p.type) { case 'file': spawnArgs.push(`--file-uri=${p.path}`); @@ -629,14 +631,14 @@ export class RawDebugSession { } } - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const process = cp.spawn(runtimeExecutable, spawnArgs, spawnOpts); process.on('error', error => { reject(error); }); process.on('exit', code => { if (code === 0) { - resolve(); + resolve(process.pid); } else { reject(new Error(`VS Code exited with ${code}`)); } From bc5b547c0585bcbd762a0d441a2c57af3aa81e0a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 16 Jan 2019 15:36:28 +0100 Subject: [PATCH 0301/1066] fixes #66580 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/media/tree.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 640ca169306..b0c1d689ebc 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -196,7 +196,7 @@ class TreeRenderer implements IListRenderer Date: Wed, 16 Jan 2019 17:14:22 +0100 Subject: [PATCH 0302/1066] introduce tree.refresh and refactor tree.updateChildren fixes #65393 --- src/vs/base/browser/ui/list/list.ts | 52 ------------------- src/vs/base/browser/ui/tree/asyncDataTree.ts | 13 +++-- src/vs/base/browser/ui/tree/dataTree.ts | 12 ++++- src/vs/base/browser/ui/tree/indexTree.ts | 4 ++ src/vs/base/browser/ui/tree/indexTreeModel.ts | 12 +++++ src/vs/base/browser/ui/tree/objectTree.ts | 4 ++ .../base/browser/ui/tree/objectTreeModel.ts | 5 ++ src/vs/base/browser/ui/tree/tree.ts | 36 +------------ .../browser/ui/tree/asyncDataTree.test.ts | 4 +- .../parts/debug/browser/baseDebugView.ts | 12 ++--- .../parts/debug/browser/loadedScriptsView.ts | 2 +- .../debug/electron-browser/callStackView.ts | 6 +-- .../parts/debug/electron-browser/repl.ts | 2 +- .../debug/electron-browser/variablesView.ts | 14 +++-- .../electron-browser/watchExpressionsView.ts | 12 +++-- .../electron-browser/views/explorerView.ts | 2 +- .../markers/electron-browser/markersPanel.ts | 11 ++-- 17 files changed, 83 insertions(+), 120 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 55037c434ed..89af3a81a5f 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -5,8 +5,6 @@ import { GestureEvent } from 'vs/base/browser/touch'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { Event } from 'vs/base/common/event'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { IDragAndDropData } from 'vs/base/browser/dnd'; export interface IListVirtualDelegate { @@ -92,53 +90,3 @@ export interface IListDragAndDrop { onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IListDragOverReaction; drop(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void; } - -/** - * Use this renderer when you want to re-render elements on account of - * an event firing. - */ -export abstract class AbstractListRenderer implements IListRenderer { - - private renderedElements = new Map(); - private listener: IDisposable; - - constructor(onDidChange: Event) { - this.listener = onDidChange(this.onDidChange, this); - } - - renderElement(element: T, index: number, templateData: TTemplateData): void { - this.renderedElements.set(element, templateData); - } - - disposeElement(element: T, index: number, templateData: TTemplateData): void { - this.renderedElements.delete(element); - } - - private onDidChange(e: T | T[] | undefined) { - if (typeof e === 'undefined') { - this.renderedElements.forEach((templateData, element) => this.renderElement(element, -1 /* TODO@joao */, templateData)); - } else if (Array.isArray(e)) { - for (const element of e) { - this.rerender(element); - } - } else { - this.rerender(e); - } - } - - private rerender(element: T): void { - const templateData = this.renderedElements.get(element); - - if (templateData) { - this.renderElement(element, -1 /* TODO@Joao */, templateData); - } - } - - dispose(): void { - this.listener.dispose(); - } - - abstract readonly templateId: string; - abstract renderTemplate(container: HTMLElement): TTemplateData; - abstract disposeTemplate(templateData: TTemplateData): void; -} \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 33c0d3556ab..39caf7f0d1c 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -351,7 +351,7 @@ export class AsyncDataTree implements IDisposable this.tree.style(styles); } - // Data Tree + // Model getInput(): TInput | undefined { return this.root.element as TInput; @@ -365,7 +365,7 @@ export class AsyncDataTree implements IDisposable const viewStateContext = viewState && { viewState, focus: [], selection: [] } as IAsyncDataTreeViewStateContext; - await this.refresh(input, true, viewStateContext); + await this.updateChildren(input, true, viewStateContext); if (viewStateContext) { this.tree.setFocus(viewStateContext.focus); @@ -373,7 +373,7 @@ export class AsyncDataTree implements IDisposable } } - refresh(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { if (typeof this.root.element === 'undefined') { throw new Error('Tree input not set'); } @@ -381,6 +381,13 @@ export class AsyncDataTree implements IDisposable return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } + // View + + refresh(element: T): void { + const node = this.getDataNode(element); + this.tree.refresh(node); + } + // Tree getNode(element: TInput | T = this.root.element): ITreeNode { diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index f6a4006af84..4cc1d74e6b7 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -38,6 +38,8 @@ export class DataTree extends AbstractTree extends AbstractTree extends AbstractTree boolean): void { this.model.setChildren((element === this.input ? null : element) as T, this.createIterator(element, isCollapsed)); } diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 180aa553b7d..7e0b567d9d8 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -31,6 +31,10 @@ export class IndexTree extends AbstractTree>, options: IIndexTreeOptions): ITreeModel { return new IndexTreeModel(view, this.rootElement, options); } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 4c9fa0390a8..105c30ea8dc 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -126,6 +126,18 @@ export class IndexTreeModel, TFilterData = voi return Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); } + refresh(location: number[]): void { + if (location.length === 0) { + throw new Error('Invalid tree location'); + } + + const { node, listIndex, revealed } = this.getTreeNodeWithListIndex(location); + + if (revealed) { + this.list.splice(listIndex, 1, [node]); + } + } + getListIndex(location: number[]): number { return this.getTreeNodeWithListIndex(location).listIndex; } diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index c13063997ee..dcdbe74b15f 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -36,6 +36,10 @@ export class ObjectTree, TFilterData = void> extends return this.model.setChildren(element, children, onDidCreateNode, onDidDeleteNode); } + refresh(element: T): void { + this.model.refresh(element); + } + protected createModel(view: ISpliceable>, options: IObjectTreeOptions): ITreeModel { return new ObjectTreeModel(view, options); } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 3ac8b1b3353..e024cf1dd92 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -103,6 +103,11 @@ export class ObjectTreeModel, TFilterData extends Non }); } + refresh(element: T): void { + const location = this.getElementLocation(element); + this.model.refresh(location); + } + getParentElement(ref: T | null = null): T | null { const location = this.getElementLocation(ref); return this.model.getParentElement(location); diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index dea1716ebb0..8fc05270f02 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -5,9 +5,8 @@ import { Event } from 'vs/base/common/event'; import { Iterator } from 'vs/base/common/iterator'; -import { IListRenderer, AbstractListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; +import { IListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IDragAndDropData } from 'vs/base/browser/dnd'; -import { coalesce } from 'vs/base/common/arrays'; export const enum TreeVisibility { @@ -177,36 +176,3 @@ export const TreeDragOverReactions = { export interface ITreeDragAndDrop extends IListDragAndDrop { onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction; } - -/** - * Use this renderer when you want to re-render elements on account of - * an event firing. - */ -export abstract class AbstractTreeRenderer - extends AbstractListRenderer, TTemplateData> - implements ITreeRenderer { - - private elementsToNodes = new Map>(); - - constructor(onDidChange: Event) { - super(Event.map(onDidChange, e => { - if (typeof e === 'undefined') { - return undefined; - } else if (Array.isArray(e)) { - return coalesce(e.map(e => this.elementsToNodes.get(e))); - } else { - return this.elementsToNodes.get(e); - } - })); - } - - renderElement(node: ITreeNode, index: number, templateData: TTemplateData): void { - super.renderElement(node, index, templateData); - this.elementsToNodes.set(node.element, node); - } - - disposeElement(node: ITreeNode, index: number, templateData: TTemplateData): void { - this.elementsToNodes.set(node.element, node); - super.disposeElement(node, index, templateData); - } -} \ No newline at end of file diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 7e0fc7b847f..294eeb87722 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -91,14 +91,14 @@ suite('AsyncDataTree', function () { { id: 'ac' } ]; - await tree.refresh(root); + await tree.updateChildren(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); await tree.expand(_('a')); assert.equal(container.querySelectorAll('.monaco-list-row').length, 4); _('a').children = []; - await tree.refresh(root); + await tree.updateChildren(root); assert.equal(container.querySelectorAll('.monaco-list-row').length, 1); }); }); \ No newline at end of file diff --git a/src/vs/workbench/parts/debug/browser/baseDebugView.ts b/src/vs/workbench/parts/debug/browser/baseDebugView.ts index b574dca5f88..3edeaf4a5cc 100644 --- a/src/vs/workbench/parts/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/parts/debug/browser/baseDebugView.ts @@ -8,7 +8,7 @@ import { IExpression, IDebugService } from 'vs/workbench/parts/debug/common/debu import { Expression, Variable } from 'vs/workbench/parts/debug/common/debugModel'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInputValidationOptions, InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { ITreeRenderer, ITreeNode, AbstractTreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; @@ -124,17 +124,13 @@ export interface IExpressionTemplateData { toDispose: IDisposable[]; } -export abstract class AbstractExpressionsRenderer - extends AbstractTreeRenderer - implements ITreeRenderer, IDisposable { +export abstract class AbstractExpressionsRenderer implements ITreeRenderer { constructor( @IDebugService protected debugService: IDebugService, @IContextViewService private readonly contextViewService: IContextViewService, @IThemeService private readonly themeService: IThemeService - ) { - super(debugService.getViewModel().onDidSelectExpression); - } + ) { } abstract get templateId(): string; @@ -201,8 +197,6 @@ export abstract class AbstractExpressionsRenderer } renderElement(node: ITreeNode, index: number, data: IExpressionTemplateData): void { - super.renderElement(node, index, data); - const { element } = node; if (element === this.debugService.getViewModel().getSelectedExpression()) { data.enableInputBox(element, this.getInputBoxOptions(element)); diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index 3849c3c194c..c8584cb4ef3 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -422,7 +422,7 @@ export class LoadedScriptsView extends ViewletPanel { this.changeScheduler = new RunOnceScheduler(() => { this.treeNeedsRefreshOnVisible = false; if (this.tree) { - this.tree.refresh(); + this.tree.updateChildren(); } }, 300); this.disposables.push(this.changeScheduler); diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 702e4fa5007..06797efabb7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -85,7 +85,7 @@ export class CallStackView extends ViewletPanel { this.needsRefresh = false; this.dataSource.deemphasizedStackFramesToShow = []; - this.tree.refresh().then(() => this.updateTreeSelection()); + this.tree.updateChildren().then(() => this.updateTreeSelection()); }, 50); } @@ -179,12 +179,12 @@ export class CallStackView extends ViewletPanel { const thread = session && session.getThread(element.threadId); if (thread) { (thread).fetchCallStack() - .then(() => this.tree.refresh()); + .then(() => this.tree.updateChildren()); } } if (element instanceof Array) { this.dataSource.deemphasizedStackFramesToShow.push(...element); - this.tree.refresh(); + this.tree.updateChildren(); } })); diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index a2fbaafc01a..5183a792b49 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -330,7 +330,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati return; } const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight; - this.tree.refresh().then(() => { + this.tree.updateChildren().then(() => { if (lastElementVisible) { // Only scroll if we were scrolled all the way down before tree refreshed #10486 revealLastElement(this.tree); diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index 3a56a1d9d92..a1c4b9b5666 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -55,7 +55,7 @@ export class VariablesView extends ViewletPanel { // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; - this.tree.refresh().then(() => { + this.tree.updateChildren().then(() => { const stackFrame = this.debugService.getViewModel().focusedStackFrame; if (stackFrame) { stackFrame.getScopes().then(scopes => { @@ -82,14 +82,13 @@ export class VariablesView extends ViewletPanel { keyboardNavigationLabelProvider: { getKeyboardNavigationLabel: e => e } }, this.contextKeyService, this.listService, this.themeService, this.configurationService, this.keybindingService); - // TODO@isidor this is a promise - this.tree.setInput(this.debugService.getViewModel()); + this.tree.setInput(this.debugService.getViewModel()).then(null, onUnexpectedError); CONTEXT_VARIABLES_FOCUSED.bindTo(this.contextKeyService.createScoped(treeContainer)); const collapseAction = new CollapseAction2(this.tree, true, 'explorer-action collapse-explorer'); this.toolbar.setActions([collapseAction])(); - this.tree.refresh(); + this.tree.updateChildren(); this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(sf => { if (!this.isBodyVisible()) { @@ -102,7 +101,7 @@ export class VariablesView extends ViewletPanel { const timeout = sf.explicit ? 0 : undefined; this.onFocusStackFrameScheduler.schedule(timeout); })); - this.disposables.push(variableSetEmitter.event(() => this.tree.refresh())); + this.disposables.push(variableSetEmitter.event(() => this.tree.updateChildren())); this.disposables.push(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); this.disposables.push(this.tree.onContextMenu(e => this.onContextMenu(e))); @@ -111,6 +110,11 @@ export class VariablesView extends ViewletPanel { this.onFocusStackFrameScheduler.schedule(); } })); + this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(e => { + if (e instanceof Variable) { + this.tree.refresh(e); + } + })); } layoutBody(size: number): void { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index f86593cc33c..409b49ad7ff 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -54,7 +54,7 @@ export class WatchExpressionsView extends ViewletPanel { this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; - this.tree.refresh(); + this.tree.updateChildren(); }, 50); } @@ -64,7 +64,6 @@ export class WatchExpressionsView extends ViewletPanel { CONTEXT_WATCH_EXPRESSIONS_FOCUSED.bindTo(this.contextKeyService.createScoped(treeContainer)); const expressionsRenderer = this.instantiationService.createInstance(WatchExpressionsRenderer); - this.disposables.push(expressionsRenderer); this.tree = new WorkbenchAsyncDataTree(treeContainer, new WatchExpressionsDelegate(), [expressionsRenderer, this.instantiationService.createInstance(VariablesRenderer)], new WatchExpressionsDataSource(), { ariaLabel: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'watchAriaTreeLabel' }, "Debug Watch Expressions"), @@ -87,7 +86,7 @@ export class WatchExpressionsView extends ViewletPanel { if (!this.isBodyVisible()) { this.needsRefresh = true; } else { - this.tree.refresh(); + this.tree.updateChildren(); } })); this.disposables.push(this.debugService.getViewModel().onDidFocusStackFrame(() => { @@ -100,13 +99,18 @@ export class WatchExpressionsView extends ViewletPanel { this.onWatchExpressionsUpdatedScheduler.schedule(); } })); - this.disposables.push(variableSetEmitter.event(() => this.tree.refresh())); + this.disposables.push(variableSetEmitter.event(() => this.tree.updateChildren())); this.disposables.push(this.onDidChangeBodyVisibility(visible => { if (visible && this.needsRefresh) { this.onWatchExpressionsUpdatedScheduler.schedule(); } })); + this.disposables.push(this.debugService.getViewModel().onDidSelectExpression(e => { + if (e instanceof Expression && e.name) { + this.tree.refresh(e); + } + })); } layoutBody(size: number): void { 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 9729d0ae69f..470e91e87b9 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -398,7 +398,7 @@ export class ExplorerView extends ViewletPanel { const recursive = !item; const toRefresh = item || this.tree.getInput(); - return this.tree.refresh(toRefresh, recursive); + return this.tree.updateChildren(toRefresh, recursive); } getOptimalWidth(): number { diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 7f28670dbc0..bd0486b90ee 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -203,10 +203,15 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { return false; } - private refreshPanel(): void { + private refreshPanel(marker?: Marker): void { if (this.isVisible()) { this.cachedFilterStats = undefined; - this.tree.setChildren(null, createModelIterator(this.markersWorkbenchService.markersModel)); + + if (marker) { + this.tree.refresh(marker); + } else { + this.tree.setChildren(null, createModelIterator(this.markersWorkbenchService.markersModel)); + } const { total, filtered } = this.getFilterStats(); dom.toggleClass(this.treeContainer, 'hidden', total > 0 && filtered === 0); @@ -216,7 +221,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { } private onDidChangeViewState(marker?: Marker): void { - this.refreshPanel(); + this.refreshPanel(marker); } private updateFilter() { From 5b443958a108be72b50dd61bae6e13eb101360d5 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 16 Jan 2019 17:50:57 +0100 Subject: [PATCH 0303/1066] explorer: Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection fixes #66589. --- .../parts/files/electron-browser/views/explorerView.ts | 9 +++++++-- 1 file changed, 7 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 470e91e87b9..f87a6a88c14 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -64,6 +64,7 @@ export class ExplorerView extends ViewletPanel { private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; private ignoreActiveEditorChange; + private previousSelection: ExplorerItem[] = []; constructor( options: IViewletPanelOptions, @@ -291,8 +292,12 @@ export class ExplorerView extends ViewletPanel { return; } const selection = e.elements; - // Do not react if the user is expanding selection - if (selection && selection.length === 1) { + const wasSelected = this.previousSelection.indexOf(selection[0]) >= 0; + this.previousSelection = selection; + // Do not react if the user is expanding selection. + // Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589. + if (selection.length === 1 && !wasSelected) { + // Do not react if user is clicking on explorer items which are input placeholders if (!selection[0].name) { // Do not react if user is clicking on explorer items which are input placeholders return; From 041b930051b9c4f6c4d4f746e41f69a618ef9936 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 16 Jan 2019 08:58:34 -0800 Subject: [PATCH 0304/1066] Fix #66588 --- src/vs/workbench/parts/search/browser/searchView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 84720cfd68e..6a07b29ff17 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -1309,7 +1309,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } else if (hasExcludes) { message = nls.localize('noResultsExcludes', "No results found excluding '{0}' - ", excludePatternText); } else { - message = nls.localize('noResultsFound', "No results found. Review your settings for configured exclusions and ignore files - "); + message = nls.localize('noResultsFound', "No results found. Review your settings for configured exclusions and check your gitignore files - "); } // Indicate as status to ARIA From 651f624f06b1a50424ac1cfcc4a3e05e1c3b52a5 Mon Sep 17 00:00:00 2001 From: alexet Date: Wed, 16 Jan 2019 17:40:47 +0000 Subject: [PATCH 0305/1066] Remove spurious pipes from regexes --- extensions/emmet/src/abbreviationActions.ts | 4 ++-- src/vs/base/common/strings.ts | 2 +- src/vs/workbench/parts/search/browser/openAnythingHandler.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index dbde660d9e5..4ee8e221f99 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -7,8 +7,8 @@ import * as vscode from 'vscode'; import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode'; import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag } from './util'; -const trimRegex = /[\u00a0]*[\d|#|\-|\*|\u2022]+\.?/; -const hexColorRegex = /^#[\d,a-f,A-F]{0,6}$/; +const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/; +const hexColorRegex = /^#[\da-fA-F]{0,6}$/; const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo', 'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q', diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index e46f0ee3937..39da4f5446f 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -56,7 +56,7 @@ export function format(value: string, ...args: any[]): string { * being used e.g. in HTMLElement.innerHTML. */ export function escape(html: string): string { - return html.replace(/[<|>|&]/g, function (match) { + return html.replace(/[<>&]/g, function (match) { switch (match) { case '<': return '<'; case '>': return '>'; diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 06d934a7501..d6242f8f593 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -32,7 +32,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { static readonly ID = 'workbench.picker.anything'; - private static readonly LINE_COLON_PATTERN = /[#|:|\(](\d*)([#|:|,](\d*))?\)?$/; + private static readonly LINE_COLON_PATTERN = /[#|:\(](\d*)([#|:,](\d*))?\)?$/; private static readonly TYPING_SEARCH_DELAY = 200; // This delay accommodates for the user typing a word and then stops typing to start searching From 0798d13f10b193df0297e301affe761b90a8bfa9 Mon Sep 17 00:00:00 2001 From: alexet Date: Wed, 16 Jan 2019 17:54:39 +0000 Subject: [PATCH 0306/1066] Remove duplicate character from regex class --- extensions/markdown-language-features/src/slugify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/src/slugify.ts b/extensions/markdown-language-features/src/slugify.ts index 5b9146b602f..a91c5126a22 100644 --- a/extensions/markdown-language-features/src/slugify.ts +++ b/extensions/markdown-language-features/src/slugify.ts @@ -23,7 +23,7 @@ export const githubSlugifier: Slugifier = new class implements Slugifier { heading.trim() .toLowerCase() .replace(/\s+/g, '-') // Replace whitespace with - - .replace(/[\]\[\!\'\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators + .replace(/[\]\[\!\'\#\$\%\&\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~\`。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}]/g, '') // Remove known punctuators .replace(/^\-+/, '') // Remove leading - .replace(/\-+$/, '') // Remove trailing - ); From 221da42e99c5bf0fc33cc074dd2b4389c7353623 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 16 Jan 2019 10:23:51 -0800 Subject: [PATCH 0307/1066] Update recent menu items on formatter registration --- src/vs/workbench/browser/parts/titlebar/menubarControl.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index f76c39c936f..dee1daa5293 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -262,6 +262,9 @@ export class MenubarControl extends Disposable { this.menubar.blur(); })); } + + // Update recent menu items on formatter registration + this._register(this.labelService.onDidRegisterFormatter(() => { this.onRecentlyOpenedChange(); })); } private doUpdateMenubar(firstTime: boolean): void { From 5ac02a091025cd6f835129d34bdaa3b3817276e6 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Jan 2019 11:13:25 -0800 Subject: [PATCH 0308/1066] Update emmet-helper for #63384 and #62855 --- extensions/emmet/package.json | 3 ++- extensions/emmet/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 8733fbdb5c9..00782109fe4 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -434,6 +434,7 @@ } }, "scripts": { + "watch": "gulp watch-extension:emmet", "compile": "gulp compile-extension:emmet" }, "devDependencies": { @@ -447,6 +448,6 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.12" + "vscode-emmet-helper": "^1.2.13" } } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 22f16e39770..f4b4824eac4 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2469,10 +2469,10 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-emmet-helper@^1.2.12: - version "1.2.12" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.12.tgz#6a75d5a74af735db179c708a9a91feb191f3edb5" - integrity sha512-pCo+BNgP3lGuzTIGP1IMOGILpNGVopiAu02FrDC4ctEQQ5vFIaMtI4HxDGAw2Zu7pqGEHn6Z1KFmOb2Hd/xkHA== +vscode-emmet-helper@^1.2.13: + version "1.2.13" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.13.tgz#fbfd15af802250279eac24c258f43c902fb964ce" + integrity sha512-88sl6KW7gJtc4tVDrNejnt0qJqd3m/ZThr5BqTTYBTIssAfnNn6SmzEur27pjJcr+zDSMIhDFIyQj/bbQr5Dow== dependencies: "@emmetio/extract-abbreviation" "0.1.6" jsonc-parser "^1.0.0" From 792ccfd3f5f983cbfd18d7dc3b2779c24c70414d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 10:54:24 -0800 Subject: [PATCH 0309/1066] Strict null auto add --- src/tsconfig.strictNullChecks.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index eb1e4a4f320..97419beb1b7 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -17,7 +17,7 @@ "./vs/editor/contrib/gotoError/**/*.ts", "./vs/editor/contrib/inPlaceReplace/**/*.ts", "./vs/editor/contrib/suggest/**/*.ts", - "./vs/editor/contrib/snippet/**/*.ts", + "./vs/editor/contrib/snippet/**/*.ts" ], "files": [ "./vs/base/parts/contextmenu/common/contextmenu.ts", @@ -576,6 +576,7 @@ "./vs/workbench/parts/codeEditor/electron-browser/languageConfiguration/languageConfigurationExtensionPoint.ts", "./vs/workbench/parts/codeEditor/electron-browser/largeFileOptimizations.ts", "./vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts", + "./vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts", "./vs/workbench/parts/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts", "./vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts", "./vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts", @@ -585,6 +586,7 @@ "./vs/workbench/parts/codeEditor/electron-browser/toggleWordWrap.ts", "./vs/workbench/parts/comments/common/commentModel.ts", "./vs/workbench/parts/comments/electron-browser/commentGlyphWidget.ts", + "./vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts", "./vs/workbench/parts/debug/browser/debugANSIHandling.ts", "./vs/workbench/parts/debug/browser/linkDetector.ts", "./vs/workbench/parts/debug/node/telemetryApp.ts", From acbcd7e16cbfeae10f03e976b938731d71b0f0e6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 11:08:10 -0800 Subject: [PATCH 0310/1066] Strict null check listService --- src/tsconfig.strictNullChecks.json | 5 +++++ src/vs/base/parts/tree/browser/treeImpl.ts | 24 +++++++++++--------- src/vs/platform/list/browser/listService.ts | 25 ++++++++++++--------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 97419beb1b7..5b05fc4c7e5 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -40,6 +40,7 @@ "./vs/base/parts/tree/browser/tree.ts", "./vs/base/parts/tree/browser/treeDefaults.ts", "./vs/base/parts/tree/browser/treeDnd.ts", + "./vs/base/parts/tree/browser/treeImpl.ts", "./vs/base/parts/tree/browser/treeModel.ts", "./vs/base/parts/tree/browser/treeUtils.ts", "./vs/base/parts/tree/browser/treeView.ts", @@ -263,6 +264,7 @@ "./vs/editor/standalone/browser/simpleServices.ts", "./vs/editor/standalone/browser/standaloneCodeEditor.ts", "./vs/editor/standalone/browser/standaloneCodeServiceImpl.ts", + "./vs/editor/standalone/browser/standaloneServices.ts", "./vs/editor/standalone/browser/standaloneThemeServiceImpl.ts", "./vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts", "./vs/editor/standalone/common/monarch/monarchCommon.ts", @@ -413,6 +415,7 @@ "./vs/platform/lifecycle/common/lifecycle.ts", "./vs/platform/lifecycle/electron-browser/lifecycleService.ts", "./vs/platform/lifecycle/electron-main/lifecycleMain.ts", + "./vs/platform/list/browser/listService.ts", "./vs/platform/localizations/common/localizations.ts", "./vs/platform/localizations/node/localizations.ts", "./vs/platform/localizations/node/localizationsIpc.ts", @@ -545,6 +548,7 @@ "./vs/workbench/browser/parts/statusbar/statusbarPart.ts", "./vs/workbench/browser/parts/views/panelViewlet.ts", "./vs/workbench/browser/parts/views/views.ts", + "./vs/workbench/browser/parts/views/viewsViewlet.ts", "./vs/workbench/browser/viewlet.ts", "./vs/workbench/common/actions.ts", "./vs/workbench/common/activity.ts", @@ -603,6 +607,7 @@ "./vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts", "./vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts", "./vs/workbench/parts/experiments/node/experimentService.ts", + "./vs/workbench/parts/extensions/browser/extensionsViewer.ts", "./vs/workbench/parts/extensions/common/extensionQuery.ts", "./vs/workbench/parts/extensions/common/extensions.ts", "./vs/workbench/parts/extensions/common/extensionsFileTemplate.ts", diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index 1bd18fb65ff..652c59d17a0 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -20,13 +20,13 @@ export class TreeContext implements _.ITreeContext { public options: _.ITreeOptions; public dataSource: _.IDataSource; - public renderer: _.IRenderer; + public renderer?: _.IRenderer; public controller: _.IController; public dnd: _.IDragAndDrop; public filter: _.IFilter; - public sorter: _.ISorter; + public sorter?: _.ISorter; public accessibilityProvider: _.IAccessibilityProvider; - public styler: _.ITreeStyler; + public styler?: _.ITreeStyler; constructor(tree: _.ITree, configuration: _.ITreeConfiguration, options: _.ITreeOptions = {}) { this.tree = tree; @@ -42,9 +42,9 @@ export class TreeContext implements _.ITreeContext { this.controller = configuration.controller || new TreeDefaults.DefaultController({ clickBehavior: TreeDefaults.ClickBehavior.ON_MOUSE_UP, keyboardSupport: typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport }); this.dnd = configuration.dnd || new TreeDefaults.DefaultDragAndDrop(); this.filter = configuration.filter || new TreeDefaults.DefaultFilter(); - this.sorter = configuration.sorter || null; + this.sorter = configuration.sorter; this.accessibilityProvider = configuration.accessibilityProvider || new TreeDefaults.DefaultAccessibilityProvider(); - this.styler = configuration.styler || null; + this.styler = configuration.styler; } } @@ -162,8 +162,10 @@ export class Tree implements _.ITree { } public updateWidth(element: any): void { - let item = this.model.getItem(element); - return this.view.updateWidth(item); + const item = this.model.getItem(element); + if (item) { + this.view.updateWidth(item); + } } public expand(element: any): Promise { @@ -203,8 +205,8 @@ export class Tree implements _.ITree { } public getRelativeTop(element: any): number { - let item = this.model.getItem(element); - return this.view.getRelativeTop(item); + const item = this.model.getItem(element); + return item ? this.view.getRelativeTop(item) : 0; } public getFirstVisibleElement(): any { @@ -377,11 +379,11 @@ export class Tree implements _.ITree { if (this.model !== null) { this.model.dispose(); - this.model = null; + this.model = null!; // StrictNullOverride Nulling out ok in dispose } if (this.view !== null) { this.view.dispose(); - this.view = null; + this.view = null!; // StrictNullOverride Nulling out ok in dispose } this._onDidChangeFocus.dispose(); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index d656dfc8e45..3ae319b4e66 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -588,10 +588,13 @@ export interface IResourceResultsNavigationOptions { export class TreeResourceNavigator2 extends Disposable { - private readonly _openResource: Emitter> = new Emitter>(); - readonly openResource: Event> = this._openResource.event; + private readonly _openResource = new Emitter>(); + readonly openResource: Event> = this._openResource.event; - constructor(private tree: WorkbenchObjectTree | WorkbenchAsyncDataTree, private options?: IResourceResultsNavigationOptions) { + constructor( + private tree: WorkbenchObjectTree | WorkbenchAsyncDataTree, + private options?: IResourceResultsNavigationOptions + ) { super(); this.registerListeners(); @@ -605,9 +608,9 @@ export class TreeResourceNavigator2 extends Disposable { this._register(this.tree.onDidChangeSelection(e => this.onSelection(e))); } - private onFocus(e: ITreeEvent): void { + private onFocus(e: ITreeEvent): void { const focus = this.tree.getFocus(); - this.tree.setSelection(focus, e.browserEvent); + this.tree.setSelection(focus as T[], e.browserEvent); if (!e.browserEvent) { return; @@ -620,7 +623,7 @@ export class TreeResourceNavigator2 extends Disposable { } } - private onSelection(e: ITreeEvent): void { + private onSelection(e: ITreeEvent): void { if (!e.browserEvent) { return; } @@ -686,8 +689,8 @@ export class HighlightingTreeController extends WorkbenchTreeController { class HightlightsFilter implements IFilter { static add(config: ITreeConfiguration, options: IHighlightingTreeOptions): ITreeConfiguration { - let myFilter = new HightlightsFilter(); - myFilter.enabled = options.filterOnType; + const myFilter = new HightlightsFilter(); + myFilter.enabled = !!options.filterOnType; if (!config.filter) { config.filter = myFilter; } else { @@ -809,7 +812,7 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { layout(height?: number, width?: number): void { this.input.layout(); - super.layout(isNaN(height) ? height : height - getTotalHeight(this.inputContainer), width); + super.layout(typeof height !== 'number' || isNaN(height) ? height : height - getTotalHeight(this.inputContainer), width); } private onTypeInTree(): void { @@ -835,7 +838,7 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { let topElement: any; if (pattern) { let nav = this.getNavigator(undefined, false); - let topScore: FuzzyScore; + let topScore: FuzzyScore | undefined; while (nav.next()) { let element = nav.current(); let score = this.highlighter.getHighlights(this, element, pattern); @@ -867,7 +870,7 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { return this.highlights.size > 0; } - getHighlighterScore(element: any): FuzzyScore { + getHighlighterScore(element: any): FuzzyScore | undefined { return this.highlights.get(this._getHighlightsStorageKey(element)); } From 82e9561f169114b81c5d0d785f439d4a0d7fd8bd Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 11:26:24 -0800 Subject: [PATCH 0311/1066] Strict null checks --- src/tsconfig.strictNullChecks.json | 8 ++++++++ src/vs/editor/standalone/browser/standaloneEditor.ts | 7 ++++--- src/vs/editor/standalone/browser/standaloneLanguages.ts | 4 ++-- .../api/browser/viewsContainersExtensionPoint.ts | 2 +- .../browser/parts/notifications/notificationsCommands.ts | 2 +- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 5b05fc4c7e5..6db7c93c154 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -256,6 +256,7 @@ "./vs/editor/contrib/wordPartOperations/test/wordPartOperations.test.ts", "./vs/editor/contrib/wordPartOperations/wordPartOperations.ts", "./vs/editor/contrib/zoneWidget/zoneWidget.ts", + "./vs/editor/editor.api.ts", "./vs/editor/editor.worker.ts", "./vs/editor/standalone/browser/accessibilityHelp/accessibilityHelp.ts", "./vs/editor/standalone/browser/colorizer.ts", @@ -264,6 +265,8 @@ "./vs/editor/standalone/browser/simpleServices.ts", "./vs/editor/standalone/browser/standaloneCodeEditor.ts", "./vs/editor/standalone/browser/standaloneCodeServiceImpl.ts", + "./vs/editor/standalone/browser/standaloneEditor.ts", + "./vs/editor/standalone/browser/standaloneLanguages.ts", "./vs/editor/standalone/browser/standaloneServices.ts", "./vs/editor/standalone/browser/standaloneThemeServiceImpl.ts", "./vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast.ts", @@ -274,6 +277,7 @@ "./vs/editor/standalone/common/standaloneThemeService.ts", "./vs/editor/standalone/common/themes.ts", "./vs/editor/standalone/test/browser/simpleServices.test.ts", + "./vs/editor/standalone/test/browser/standaloneLanguages.test.ts", "./vs/editor/test/browser/controller/imeTester.ts", "./vs/editor/test/browser/editorTestServices.ts", "./vs/editor/test/browser/testCodeEditor.ts", @@ -508,6 +512,7 @@ "./vs/platform/workspaces/node/workspacesIpc.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", + "./vs/workbench/api/browser/viewsContainersExtensionPoint.ts", "./vs/workbench/api/node/extHostExtensionActivator.ts", "./vs/workbench/api/node/extHostSearch.fileIndex.ts", "./vs/workbench/api/node/extHostTypes.ts", @@ -539,7 +544,10 @@ "./vs/workbench/browser/parts/editor/resourceViewer.ts", "./vs/workbench/browser/parts/editor/sideBySideEditor.ts", "./vs/workbench/browser/parts/editor/textEditor.ts", + "./vs/workbench/browser/parts/notifications/notificationsActions.ts", "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", + "./vs/workbench/browser/parts/notifications/notificationsCommands.ts", + "./vs/workbench/browser/parts/notifications/notificationsStatus.ts", "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", "./vs/workbench/browser/parts/quickopen/quickopen.ts", diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 638277b52d2..30ecaf3b7d9 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -37,6 +37,8 @@ import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +type Omit = Pick>; + function withAllStandaloneServices(domElement: HTMLElement, override: IEditorOverrideServices, callback: (services: DynamicStandaloneServices) => T): T { let services = new DynamicStandaloneServices(domElement, override); @@ -260,15 +262,14 @@ export function colorizeModelLine(model: ITextModel, lineNumber: number, tabSize /** * @internal */ -function getSafeTokenizationSupport(language: string): modes.ITokenizationSupport { +function getSafeTokenizationSupport(language: string): Omit { let tokenizationSupport = modes.TokenizationRegistry.get(language); if (tokenizationSupport) { return tokenizationSupport; } return { getInitialState: () => NULL_STATE, - tokenize: (line: string, state: modes.IState, deltaOffset: number) => nullTokenize(language, line, state, deltaOffset), - tokenize2: undefined, + tokenize: (line: string, state: modes.IState, deltaOffset: number) => nullTokenize(language, line, state, deltaOffset) }; } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index e5ffa21d894..238ae5df16e 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -345,10 +345,10 @@ export function registerSignatureHelpProvider(languageId: string, provider: mode */ export function registerHoverProvider(languageId: string, provider: modes.HoverProvider): IDisposable { return modes.HoverProviderRegistry.register(languageId, { - provideHover: (model: model.ITextModel, position: Position, token: CancellationToken): Promise => { + provideHover: (model: model.ITextModel, position: Position, token: CancellationToken): Promise => { let word = model.getWordAtPosition(position); - return Promise.resolve(provider.provideHover(model, position, token)).then((value) => { + return Promise.resolve(provider.provideHover(model, position, token)).then((value): modes.Hover | undefined => { if (!value) { return undefined; } diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 0e24d882303..b732ccd81d6 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -153,7 +153,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { return order; } - private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: ExtensionIdentifier): void { + private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): void { const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); const id = descriptor.id; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 1f5f7050de4..016b50dc100 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -62,7 +62,7 @@ export interface INotificationsToastController { export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController): void { - function getNotificationFromContext(listService: IListService, context?: any): INotificationViewItem { + function getNotificationFromContext(listService: IListService, context?: any): INotificationViewItem | undefined { if (isNotificationViewItem(context)) { return context; } From c549276df8588405dd98dc5d174b6975fb0678a9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 11:46:15 -0800 Subject: [PATCH 0312/1066] Strict null check references widget --- src/tsconfig.strictNullChecks.json | 2 ++ .../referenceSearch/referencesWidget.ts | 25 +++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 6db7c93c154..b5b78657dc6 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -245,6 +245,7 @@ "./vs/editor/contrib/referenceSearch/peekViewWidget.ts", "./vs/editor/contrib/referenceSearch/referencesModel.ts", "./vs/editor/contrib/referenceSearch/referencesTree.ts", + "./vs/editor/contrib/referenceSearch/referencesWidget.ts", "./vs/editor/contrib/referenceSearch/test/referencesModel.test.ts", "./vs/editor/contrib/rename/rename.ts", "./vs/editor/contrib/rename/renameInputField.ts", @@ -872,6 +873,7 @@ "./vs/workbench/services/themes/common/workbenchThemeService.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeData.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts", + "./vs/workbench/services/themes/electron-browser/themeCompatibility.ts", "./vs/workbench/services/timer/electron-browser/timerService.ts", "./vs/workbench/services/title/common/titleService.ts", "./vs/workbench/services/viewlet/browser/viewlet.ts", diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 8bfb904fcad..6a3e9a797ac 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -72,6 +72,9 @@ class DecorationsManager implements IDisposable { } private _addDecorations(reference: FileReferences): void { + if (!this._editor.hasModel()) { + return; + } this._callOnModelChange.push(this._editor.getModel().onDidChangeDecorations((event) => this._onDecorationChanged())); const newDecorations: IModelDeltaDecoration[] = []; @@ -98,8 +101,13 @@ class DecorationsManager implements IDisposable { private _onDecorationChanged(): void { const toRemove: string[] = []; + const model = this._editor.getModel(); + if (!model) { + return; + } + this._decorations.forEach((reference, decorationId) => { - const newRange = this._editor.getModel().getDecorationRange(decorationId); + const newRange = model.getDecorationRange(decorationId); if (!newRange) { return; @@ -220,7 +228,7 @@ export interface LayoutData { export interface SelectionEvent { kind: 'goto' | 'show' | 'side' | 'open'; source: 'editor' | 'tree' | 'title'; - element: Location; + element?: Location; } export const ctxReferenceWidgetSearchTreeFocused = new RawContextKey('referenceSearchTreeFocused', true); @@ -424,7 +432,7 @@ export class ReferenceWidget extends PeekViewWidget { // store layout data this.layoutData = { - heightInLines: this._viewZone.heightInLines, + heightInLines: this._viewZone ? this._viewZone.heightInLines : 0, ratio: this._sash.ratio }; } @@ -446,7 +454,7 @@ export class ReferenceWidget extends PeekViewWidget { }); } - public setModel(newModel: ReferencesModel | undefined): Promise { + public setModel(newModel: ReferencesModel | undefined): Promise | undefined { // clean up this._disposeOnNewModel = dispose(this._disposeOnNewModel); this._model = newModel; @@ -457,6 +465,9 @@ export class ReferenceWidget extends PeekViewWidget { } private _onNewModel(): Promise { + if (!this._model) { + return Promise.resolve(undefined); + } if (this._model.empty) { this.setTitle(''); @@ -483,7 +494,7 @@ export class ReferenceWidget extends PeekViewWidget { return; } this._onDidSelectReference.fire({ - element: { uri: element.uri, range: target.range }, + element: { uri: element.uri, range: target.range! }, kind: (event.ctrlKey || event.metaKey || event.altKey) ? 'side' : 'open', source: 'editor' }); @@ -501,7 +512,7 @@ export class ReferenceWidget extends PeekViewWidget { return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model); } - private _getFocusedReference(): OneReference { + private _getFocusedReference(): OneReference | undefined { const [element] = this._tree.getFocus(); if (element instanceof OneReference) { return element; @@ -525,7 +536,7 @@ export class ReferenceWidget extends PeekViewWidget { // Update widget header if (reference.uri.scheme !== Schemas.inMemory) { - this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri))); + this.setTitle(basenameOrAuthority(reference.uri), this._uriLabel.getUriLabel(dirname(reference.uri)!)); } else { this.setTitle(nls.localize('peekView.alternateTitle', "References")); } From 2ed9fa8eee4652b3b26e0b1bccfacf1a9ccfe26b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 11:50:49 -0800 Subject: [PATCH 0313/1066] Strict null checks themes.contribution --- src/tsconfig.strictNullChecks.json | 1 + .../themes/electron-browser/themes.contribution.ts | 14 ++++++++------ .../themes/common/workbenchThemeService.ts | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index b5b78657dc6..a51f3a265df 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -730,6 +730,7 @@ "./vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts", "./vs/workbench/parts/terminal/test/node/terminalCommandTracker.test.ts", "./vs/workbench/parts/terminal/test/node/terminalEnvironment.test.ts", + "./vs/workbench/parts/themes/electron-browser/themes.contribution.ts", "./vs/workbench/parts/url/electron-browser/url.contribution.ts", "./vs/workbench/parts/webview/electron-browser/webviewProtocols.ts", "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts", diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 6df1b20da3d..9a7177130d5 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -46,7 +46,7 @@ export class SelectColorThemeAction extends Action { return this.themeService.getColorThemes().then(themes => { const currentTheme = this.themeService.getColorTheme(); - const picks: QuickPickInput[] = [].concat( + const picks: QuickPickInput[] = ([] as QuickPickInput[]).concat( toEntries(themes.filter(t => t.type === LIGHT), localize('themes.category.light', "light themes")), toEntries(themes.filter(t => t.type === DARK), localize('themes.category.dark', "dark themes")), toEntries(themes.filter(t => t.type === HIGH_CONTRAST), localize('themes.category.hc', "high contrast themes")), @@ -60,7 +60,7 @@ export class SelectColorThemeAction extends Action { } theme = currentTheme; } - let target = null; + let target: ConfigurationTarget | null = null; if (applyTheme) { let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; @@ -121,7 +121,7 @@ class SelectIconThemeAction extends Action { } theme = currentTheme; } - let target = null; + let target: ConfigurationTarget | undefined = undefined; if (applyTheme) { let confValue = this.configurationService.inspect(ICON_THEME_SETTING); target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; @@ -129,7 +129,7 @@ class SelectIconThemeAction extends Action { this.themeService.setFileIconTheme(theme && theme.id, target).then(undefined, err => { onUnexpectedError(err); - this.themeService.setFileIconTheme(currentTheme.id, null); + this.themeService.setFileIconTheme(currentTheme.id, undefined); } ); }; @@ -164,8 +164,10 @@ function configurationEntries(extensionGalleryService: IExtensionGalleryService, function openExtensionViewlet(viewletService: IViewletService, query: string) { return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { - (viewlet).search(query); - viewlet.focus(); + if (viewlet) { + (viewlet as IExtensionsViewlet).search(query); + viewlet.focus(); + } }); } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index fccef1aa515..e004b9d06d9 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -59,7 +59,7 @@ export interface IWorkbenchThemeService extends IThemeService { onDidColorThemeChange: Event; restoreColorTheme(); - setFileIconTheme(iconThemeId: string, settingsTarget: ConfigurationTarget): Promise; + setFileIconTheme(iconThemeId: string, settingsTarget: ConfigurationTarget | undefined): Promise; getFileIconTheme(): IFileIconTheme; getFileIconThemes(): Promise; onDidFileIconThemeChange: Event; From cc85c9c705d3c5de282ab2b53f977560a0ee2b63 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 11:54:01 -0800 Subject: [PATCH 0314/1066] Strict null check suggest enabled input --- src/tsconfig.strictNullChecks.json | 1 + .../electron-browser/suggestEnabledInput.ts | 33 ++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index a51f3a265df..70416bea3d5 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -591,6 +591,7 @@ "./vs/workbench/parts/codeEditor/electron-browser/selectionClipboard.ts", "./vs/workbench/parts/codeEditor/electron-browser/simpleEditorOptions.ts", "./vs/workbench/parts/codeEditor/electron-browser/sleepResumeRepaintMinimap.ts", + "./vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts", "./vs/workbench/parts/codeEditor/electron-browser/textMate/inspectTMScopes.ts", "./vs/workbench/parts/codeEditor/electron-browser/toggleMinimap.ts", "./vs/workbench/parts/codeEditor/electron-browser/toggleMultiCursorModifier.ts", diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts index b5680dc9d9f..3e2991208c4 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts @@ -99,8 +99,8 @@ export class SuggestEnabledInput extends Widget implements IThemable { private _onEnter = new Emitter(); readonly onEnter: Event = this._onEnter.event; - private _onInputDidChange = new Emitter(); - readonly onInputDidChange: Event = this._onInputDidChange.event; + private _onInputDidChange = new Emitter(); + readonly onInputDidChange: Event = this._onInputDidChange.event; private disposables: IDisposable[] = []; private inputWidget: CodeEditorWidget; @@ -120,7 +120,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { super(); this.stylingContainer = append(parent, $('.suggest-input-container')); - this.placeholderText = append(this.stylingContainer, $('.suggest-input-placeholder', null, options.placeholderText || '')); + this.placeholderText = append(this.stylingContainer, $('.suggest-input-placeholder', undefined, options.placeholderText || '')); const editorOptions: IEditorOptions = mixin( getSimpleEditorOptions(), @@ -152,13 +152,16 @@ export class SuggestEnabledInput extends Widget implements IThemable { onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)).on(() => this._onShouldFocusResults.fire(), this, this.disposables); let preexistingContent = this.getValue(); - this.disposables.push(this.inputWidget.getModel().onDidChangeContent(() => { - let content = this.getValue(); - this.placeholderText.style.visibility = content ? 'hidden' : 'visible'; - if (preexistingContent.trim() === content.trim()) { return; } - this._onInputDidChange.fire(undefined); - preexistingContent = content; - })); + const inputWidgetModel = this.inputWidget.getModel(); + if (inputWidgetModel) { + this.disposables.push(inputWidgetModel.onDidChangeContent(() => { + let content = this.getValue(); + this.placeholderText.style.visibility = content ? 'hidden' : 'visible'; + if (preexistingContent.trim() === content.trim()) { return; } + this._onInputDidChange.fire(undefined); + preexistingContent = content; + })); + } let validatedSuggestProvider = { provideResults: suggestionProvider.provideResults, @@ -206,9 +209,9 @@ export class SuggestEnabledInput extends Widget implements IThemable { public style(colors: ISuggestEnabledInputStyles): void { - this.stylingContainer.style.backgroundColor = colors.inputBackground && colors.inputBackground.toString(); - this.stylingContainer.style.color = colors.inputForeground && colors.inputForeground.toString(); - this.placeholderText.style.color = colors.inputPlaceholderForeground && colors.inputPlaceholderForeground.toString(); + this.stylingContainer.style.backgroundColor = colors.inputBackground ? colors.inputBackground.toString() : null; + this.stylingContainer.style.color = colors.inputForeground ? colors.inputForeground.toString() : null; + this.placeholderText.style.color = colors.inputPlaceholderForeground ? colors.inputPlaceholderForeground.toString() : null; this.stylingContainer.style.borderWidth = '1px'; this.stylingContainer.style.borderStyle = 'solid'; @@ -218,7 +221,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { const cursor = this.stylingContainer.getElementsByClassName('cursor')[0] as HTMLDivElement; if (cursor) { - cursor.style.backgroundColor = colors.inputForeground && colors.inputForeground.toString(); + cursor.style.backgroundColor = colors.inputForeground ? colors.inputForeground.toString() : null; } } @@ -260,7 +263,7 @@ registerThemingParticipant((theme, collector) => { // Override inactive selection bg const inputBackgroundColor = theme.getColor(inputBackground); - if (inputBackground) { + if (inputBackgroundColor) { collector.addRule(`.suggest-input-container .monaco-editor .selected-text { background-color: ${inputBackgroundColor.transparent(0.4)}; }`); } From 9d7c89c5763798a909697403c1d90d7f8beed9bf Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 12:00:00 -0800 Subject: [PATCH 0315/1066] Strict null check files.ts --- src/tsconfig.strictNullChecks.json | 1 + src/vs/workbench/parts/files/browser/files.ts | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 70416bea3d5..0a3bd9930fa 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -631,6 +631,7 @@ "./vs/workbench/parts/feedback/electron-browser/feedback.contribution.ts", "./vs/workbench/parts/feedback/electron-browser/feedback.ts", "./vs/workbench/parts/feedback/electron-browser/feedbackStatusbarItem.ts", + "./vs/workbench/parts/files/browser/files.ts", "./vs/workbench/parts/files/common/explorerModel.ts", "./vs/workbench/parts/files/common/files.ts", "./vs/workbench/parts/files/electron-browser/explorerService.ts", diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index f745fc4a024..179b992b584 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -13,7 +13,7 @@ import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; // Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed -export function getResourceForCommand(resource: URI | object, listService: IListService, editorService: IEditorService): URI { +export function getResourceForCommand(resource: URI | object, listService: IListService, editorService: IEditorService): URI | null { if (URI.isUri(resource)) { return resource; } @@ -40,7 +40,7 @@ export function getResourceForCommand(resource: URI | object, listService: IList return toResource(editorService.activeEditor, { supportSideBySide: true }); } -export function getMultiSelectedResources(resource: URI | object, listService: IListService, editorService: IEditorService): URI[] { +export function getMultiSelectedResources(resource: URI | object, listService: IListService, editorService: IEditorService): Array { const list = listService.lastFocusedList; if (list && list.getHTMLElement() === document.activeElement) { // Explorer @@ -60,9 +60,15 @@ export function getMultiSelectedResources(resource: URI | object, listService: I const selection = list.getSelectedElements().filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource()); const focusedElements = list.getFocusedElements(); const focus = focusedElements.length ? focusedElements[0] : undefined; - const mainUriStr = URI.isUri(resource) ? resource.toString() : (focus instanceof OpenEditor) ? focus.getResource().toString() : undefined; + let mainUriStr: string | undefined = undefined; + if (URI.isUri(resource)) { + mainUriStr = resource.toString(); + } else if (focus instanceof OpenEditor) { + const focusedResource = focus.getResource(); + mainUriStr = focusedResource ? focusedResource.toString() : undefined; + } // We only respect the selection if it contains the main element. - if (selection.some(s => s.toString() === mainUriStr)) { + if (selection.some(s => !!s && s.toString() === mainUriStr)) { return selection; } } From ba0d1454e706fb39fe7812d50a0143d718058aee Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Jan 2019 15:40:30 -0800 Subject: [PATCH 0316/1066] Update emmet-helper for #65455 --- extensions/emmet/package.json | 2 +- extensions/emmet/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 00782109fe4..537911525bc 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -448,6 +448,6 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.13" + "vscode-emmet-helper": "^1.2.14" } } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index f4b4824eac4..8c10f2a7567 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2469,10 +2469,10 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-emmet-helper@^1.2.13: - version "1.2.13" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.13.tgz#fbfd15af802250279eac24c258f43c902fb964ce" - integrity sha512-88sl6KW7gJtc4tVDrNejnt0qJqd3m/ZThr5BqTTYBTIssAfnNn6SmzEur27pjJcr+zDSMIhDFIyQj/bbQr5Dow== +vscode-emmet-helper@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.14.tgz#11b97b86dbebd952a77cb278fdf921f59a36de2a" + integrity sha512-hhiWVK+FzsbSqfk07h8UYk73n2SOtxpdbGal7v+jUSsD2IbHNM2NeqEgfRY0GUtVnobufM4xbPNgaoR+MOc7pw== dependencies: "@emmetio/extract-abbreviation" "0.1.6" jsonc-parser "^1.0.0" From 38f4a0cd077593ca00fafdfb6a62ba2235bf152b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 14:09:53 -0800 Subject: [PATCH 0317/1066] Strict null check quickInputList and main.contributions --- src/tsconfig.strictNullChecks.json | 2 ++ .../ui/highlightedlabel/highlightedLabel.ts | 2 +- .../parts/quickinput/quickInputList.ts | 36 +++++++++---------- .../electron-browser/main.contribution.ts | 12 +++---- src/vs/workbench/parts/files/browser/files.ts | 7 ++-- 5 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 0a3bd9930fa..3d125d9a9d9 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -550,6 +550,7 @@ "./vs/workbench/browser/parts/notifications/notificationsCommands.ts", "./vs/workbench/browser/parts/notifications/notificationsStatus.ts", "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", + "./vs/workbench/browser/parts/quickinput/quickInputList.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", "./vs/workbench/browser/parts/quickopen/quickopen.ts", "./vs/workbench/browser/parts/sidebar/sidebarPart.ts", @@ -611,6 +612,7 @@ "./vs/workbench/parts/emmet/electron-browser/emmetActions.ts", "./vs/workbench/parts/emmet/test/electron-browser/emmetAction.test.ts", "./vs/workbench/parts/execution/common/execution.ts", + "./vs/workbench/parts/execution/electron-browser/execution.contribution.ts", "./vs/workbench/parts/execution/electron-browser/terminal.ts", "./vs/workbench/parts/execution/electron-browser/terminalService.ts", "./vs/workbench/parts/execution/test/electron-browser/terminalService.test.ts", diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index f98b11e6347..14d7439bf90 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -33,7 +33,7 @@ export class HighlightedLabel implements IDisposable { return this.domNode; } - set(text: string, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { + set(text: string | undefined, highlights: IHighlight[] = [], title: string = '', escapeNewLines?: boolean) { if (!text) { text = ''; } diff --git a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts index 876cd49cbf2..5d8f7aceeae 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInputList.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInputList.ts @@ -32,14 +32,14 @@ import { IListOptions } from 'vs/base/browser/ui/list/listWidget'; const $ = dom.$; interface IListElement { - index: number; - item: IQuickPickItem; - saneLabel: string; - saneDescription?: string; - saneDetail?: string; - checked: boolean; - separator: IQuickPickSeparator; - fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; + readonly index: number; + readonly item: IQuickPickItem; + readonly saneLabel: string; + readonly saneDescription?: string; + readonly saneDetail?: string; + readonly checked: boolean; + readonly separator?: IQuickPickSeparator; + readonly fireButtonTriggered: (event: IQuickPickItemButtonEvent) => void; } class ListElement implements IListElement { @@ -53,7 +53,7 @@ class ListElement implements IListElement { onChecked = this._onChecked.event; _checked?: boolean; get checked() { - return this._checked; + return !!this._checked; } set checked(value: boolean) { if (value !== this._checked) { @@ -61,7 +61,7 @@ class ListElement implements IListElement { this._onChecked.fire(value); } } - separator: IQuickPickSeparator; + separator?: IQuickPickSeparator; labelHighlights?: IMatch[]; descriptionHighlights?: IMatch[]; detailHighlights?: IMatch[]; @@ -173,14 +173,14 @@ class ListElementRenderer implements IListRenderer { - const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => { + const action = new Action(`id-${index}`, '', button.iconClass || (button.iconPath ? getIconClass(button.iconPath) : undefined), true, () => { element.fireButtonTriggered({ button, item: element.item }); - return null; + return Promise.resolve(); }); - action.tooltip = button.tooltip; + action.tooltip = button.tooltip || ''; return action; }), { icon: true, label: false }); dom.addClass(data.entry, 'has-actions'); @@ -395,7 +395,7 @@ export class QuickInputList { setFocusedElements(items: IQuickPickItem[]) { this.list.setFocus(items .filter(item => this.elementsToIndexes.has(item)) - .map(item => this.elementsToIndexes.get(item))); + .map(item => this.elementsToIndexes.get(item)!)); } getActiveDescendant() { @@ -410,7 +410,7 @@ export class QuickInputList { setSelectedElements(items: IQuickPickItem[]) { this.list.setSelection(items .filter(item => this.elementsToIndexes.has(item)) - .map(item => this.elementsToIndexes.get(item))); + .map(item => this.elementsToIndexes.get(item)!)); } getCheckedElements() { @@ -484,9 +484,9 @@ export class QuickInputList { // Filter by value (since we support octicons, use octicon aware fuzzy matching) else { this.elements.forEach(element => { - const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)); - const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || '')) : undefined; - const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || '')) : undefined; + const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined; + const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || '')) || undefined : undefined; + const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || '')) || undefined : undefined; if (labelHighlights || descriptionHighlights || detailHighlights) { element.labelHighlights = labelHighlights; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index e56dff6c445..e0a5e825a18 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -96,13 +96,13 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(Toggle 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, null), 'View: Navigate to the View Above', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateDownAction, NavigateDownAction.ID, NavigateDownAction.LABEL, null), 'View: Navigate to the View Below', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateLeftAction, NavigateLeftAction.ID, NavigateLeftAction.LABEL, null), 'View: Navigate to the View on the Left', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NavigateRightAction, NavigateRightAction.ID, NavigateRightAction.LABEL, null), 'View: Navigate to the View on the Right', 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, null), 'View: Increase Current View Size', viewCategory); -workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(DecreaseViewSizeAction, DecreaseViewSizeAction.ID, DecreaseViewSizeAction.LABEL, null), 'View: Decrease Current View Size', 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); diff --git a/src/vs/workbench/parts/files/browser/files.ts b/src/vs/workbench/parts/files/browser/files.ts index 179b992b584..e6953ee8700 100644 --- a/src/vs/workbench/parts/files/browser/files.ts +++ b/src/vs/workbench/parts/files/browser/files.ts @@ -10,6 +10,7 @@ import { toResource } from 'vs/workbench/common/editor'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; +import { coalesce } from 'vs/base/common/arrays'; // Commands can get exeucted from a command pallete, from a context menu or from some list using a keybinding // To cover all these cases we need to properly compute the resource on which the command is being executed @@ -40,7 +41,7 @@ export function getResourceForCommand(resource: URI | object, listService: IList return toResource(editorService.activeEditor, { supportSideBySide: true }); } -export function getMultiSelectedResources(resource: URI | object, listService: IListService, editorService: IEditorService): Array { +export function getMultiSelectedResources(resource: URI | object, listService: IListService, editorService: IEditorService): Array { const list = listService.lastFocusedList; if (list && list.getHTMLElement() === document.activeElement) { // Explorer @@ -57,7 +58,7 @@ export function getMultiSelectedResources(resource: URI | object, listService: I // Open editors view if (list instanceof List) { - const selection = list.getSelectedElements().filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource()); + const selection = coalesce(list.getSelectedElements().filter(s => s instanceof OpenEditor).map((oe: OpenEditor) => oe.getResource())); const focusedElements = list.getFocusedElements(); const focus = focusedElements.length ? focusedElements[0] : undefined; let mainUriStr: string | undefined = undefined; @@ -68,7 +69,7 @@ export function getMultiSelectedResources(resource: URI | object, listService: I mainUriStr = focusedResource ? focusedResource.toString() : undefined; } // We only respect the selection if it contains the main element. - if (selection.some(s => !!s && s.toString() === mainUriStr)) { + if (selection.some(s => s.toString() === mainUriStr)) { return selection; } } From b76ea579f6b90bde262c0f67a580a9e8162d1fe8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 14:59:34 -0800 Subject: [PATCH 0318/1066] Strict null checks --- src/tsconfig.strictNullChecks.json | 5 + src/vs/base/browser/ui/dropdown/dropdown.ts | 4 +- .../notifications/notificationsCenter.ts | 4 +- .../parts/notifications/notificationsList.ts | 4 +- .../notifications/notificationsToasts.ts | 6 +- .../notifications/notificationsViewer.ts | 25 ++--- .../electron-browser/dirtydiffDecorator.ts | 94 ++++++++++++------- 7 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 3d125d9a9d9..1bf494069ab 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -547,8 +547,12 @@ "./vs/workbench/browser/parts/editor/textEditor.ts", "./vs/workbench/browser/parts/notifications/notificationsActions.ts", "./vs/workbench/browser/parts/notifications/notificationsAlerts.ts", + "./vs/workbench/browser/parts/notifications/notificationsCenter.ts", "./vs/workbench/browser/parts/notifications/notificationsCommands.ts", + "./vs/workbench/browser/parts/notifications/notificationsList.ts", "./vs/workbench/browser/parts/notifications/notificationsStatus.ts", + "./vs/workbench/browser/parts/notifications/notificationsToasts.ts", + "./vs/workbench/browser/parts/notifications/notificationsViewer.ts", "./vs/workbench/browser/parts/quickinput/quickInputBox.ts", "./vs/workbench/browser/parts/quickinput/quickInputList.ts", "./vs/workbench/browser/parts/quickinput/quickInputUtils.ts", @@ -661,6 +665,7 @@ "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", "./vs/workbench/parts/scm/common/scm.ts", + "./vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts", "./vs/workbench/parts/scm/electron-browser/scmActivity.ts", "./vs/workbench/parts/scm/electron-browser/scmMenus.ts", "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index 397082f1584..ea595a9af9d 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -276,8 +276,8 @@ export class DropdownMenuActionItem extends BaseActionItem { private anchorAlignmentProvider: (() => AnchorAlignment) | undefined; constructor(action: IAction, menuActions: IAction[], contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); - constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: (action: IAction) => ResolvedKeybinding | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { + constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment); + constructor(action: IAction, menuActionsOrProvider: any, contextMenuProvider: IContextMenuProvider, actionItemProvider: IActionItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string, anchorAlignmentProvider?: () => AnchorAlignment) { super(null, action); this.menuActionsOrProvider = menuActionsOrProvider; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 856e21566cc..15a2d68bbf0 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -152,10 +152,10 @@ export class NotificationsCenter extends Themable { this.container.appendChild(this.notificationsCenterContainer); } - private getKeybindingLabel(action: IAction): string { + private getKeybindingLabel(action: IAction): string | null { const keybinding = this.keybindingService.lookupKeybinding(action.id); - return keybinding ? keybinding.getLabel() : undefined; + return keybinding ? keybinding.getLabel() : null; } private onDidNotificationChange(e: INotificationChangeEvent): void { diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index c2970931501..08669edfbf3 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -89,7 +89,7 @@ export class NotificationsList extends Themable { } this.contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, + getAnchor: () => e.anchor!, getActions: () => [copyAction], getActionsContext: () => e.element, actionRunner @@ -133,7 +133,7 @@ export class NotificationsList extends Themable { const focusedIndex = this.list.getFocus()[0]; const focusedItem = this.viewModel[focusedIndex]; - let focusRelativeTop: number; + let focusRelativeTop: number | null = null; if (typeof focusedIndex === 'number') { focusRelativeTop = this.list.getRelativeTop(focusedIndex); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 0042e24a10c..59a65ed0af1 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -456,7 +456,7 @@ export class NotificationsToasts extends Themable { let maxWidth = NotificationsToasts.MAX_WIDTH; let availableWidth = maxWidth; - let availableHeight: number; + let availableHeight: number | undefined; if (this.workbenchDimensions) { @@ -477,7 +477,9 @@ export class NotificationsToasts extends Themable { availableHeight -= (2 * 12); // adjust for paddings top and bottom } - availableHeight = Math.round(availableHeight * 0.618); // try to not cover the full height for stacked toasts + availableHeight = typeof availableHeight === 'number' + ? Math.round(availableHeight * 0.618) // try to not cover the full height for stacked toasts + : 0; return new Dimension(Math.min(maxWidth, availableWidth), availableHeight); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 58142cdbcab..b09553ed216 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -16,12 +16,13 @@ import { IAction, IActionRunner } from 'vs/base/common/actions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { DropdownMenuActionItem } from 'vs/base/browser/ui/dropdown/dropdown'; +import { DropdownMenuActionItem, IContextMenuProvider } from 'vs/base/browser/ui/dropdown/dropdown'; import { INotificationViewItem, NotificationViewItem, NotificationViewItemLabelKind, INotificationMessage, ChoiceAction } from 'vs/workbench/common/notifications'; import { ClearNotificationAction, ExpandNotificationAction, CollapseNotificationAction, ConfigureNotificationAction } from 'vs/workbench/browser/parts/notifications/notificationsActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Severity } from 'vs/platform/notification/common/notification'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; export class NotificationsListDelegate implements IListVirtualDelegate { @@ -61,7 +62,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate 0) { + if (notification.source || isNonEmptyArray(notification.actions.primary)) { expandedHeight += NotificationsListDelegate.ROW_HEIGHT; } @@ -81,7 +82,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate 0) { + if (isNonEmptyArray(notification.actions.secondary)) { actions++; // secondary actions } this.offsetHelper.style.width = `calc(100% - ${10 /* padding */ + 24 /* severity icon */ + (actions * 24) /* 24px per action */}px)`; @@ -104,7 +105,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate { - if (action instanceof ConfigureNotificationAction) { - const item = new DropdownMenuActionItem(action, action.configurationActions, this.contextMenuService, null, this.actionRunner, null, action.class); + if (action && action instanceof ConfigureNotificationAction) { + const item = new DropdownMenuActionItem(action, action.configurationActions, this.contextMenuService as IContextMenuProvider, undefined, this.actionRunner, undefined, action.class as string); data.toDispose.push(item); return item; @@ -369,7 +370,7 @@ export class NotificationTemplateRenderer { const messageOverflows = notification.canCollapse && !notification.expanded && this.template.message.scrollWidth > this.template.message.clientWidth; if (messageOverflows) { - this.template.message.title = this.template.message.textContent; + this.template.message.title = this.template.message.textContent + ''; } else { this.template.message.removeAttribute('title'); } @@ -386,7 +387,7 @@ export class NotificationTemplateRenderer { const actions: IAction[] = []; // Secondary Actions - if (notification.actions.secondary.length > 0) { + if (isNonEmptyArray(notification.actions.secondary)) { const configureNotificationAction = this.instantiationService.createInstance(ConfigureNotificationAction, ConfigureNotificationAction.ID, ConfigureNotificationAction.LABEL, notification.actions.secondary); actions.push(configureNotificationAction); this.inputDisposeables.push(configureNotificationAction); @@ -429,10 +430,10 @@ export class NotificationTemplateRenderer { private renderButtons(notification: INotificationViewItem): void { clearNode(this.template.buttonsContainer); - if (notification.expanded) { + if (notification.expanded && notification.actions.primary) { const buttonGroup = new ButtonGroup(this.template.buttonsContainer, notification.actions.primary.length, { title: true /* assign titles to buttons in case they overflow */ }); buttonGroup.buttons.forEach((button, index) => { - const action = notification.actions.primary[index]; + const action = notification.actions.primary![index]; button.label = action.label; this.inputDisposeables.push(button.onDidClick(e => { @@ -497,10 +498,10 @@ export class NotificationTemplateRenderer { } } - private getKeybindingLabel(action: IAction): string { + private getKeybindingLabel(action: IAction): string | null { const keybinding = this.keybindingService.lookupKeybinding(action.id); - return keybinding ? keybinding.getLabel() : undefined; + return keybinding ? keybinding.getLabel() : null; } dispose(): void { diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index 6dda5edb047..1921703d8a9 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -40,7 +40,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename } from 'vs/base/common/paths'; import { MenuId, IMenuService, IMenu, MenuItemAction } from 'vs/platform/actions/common/actions'; import { MenuItemActionItem, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; -import { IChange, IEditorModel, ScrollType, IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { OverviewRulerLane, ITextModel, IModelDecorationOptions } from 'vs/editor/common/model'; import { sortedDiff, firstIndex } from 'vs/base/common/arrays'; import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; @@ -77,7 +77,7 @@ class DiffActionRunner extends ActionRunner { } export interface IModelRegistry { - getModel(editorModel: IEditorModel): DirtyDiffModel; + getModel(editorModel: IEditorModel): DirtyDiffModel | null; } export const isDirtyDiffVisible = new RawContextKey('dirtyDiffVisible', false); @@ -156,7 +156,7 @@ function getChangeType(change: IChange): ChangeType { } } -function getChangeTypeColor(theme: ITheme, changeType: ChangeType): Color { +function getChangeTypeColor(theme: ITheme, changeType: ChangeType): Color | null { switch (changeType) { case ChangeType.Modify: return theme.getColor(editorGutterModifiedBackground); case ChangeType.Add: return theme.getColor(editorGutterAddedBackground); @@ -164,7 +164,7 @@ function getChangeTypeColor(theme: ITheme, changeType: ChangeType): Color { } } -function getOuterEditorFromDiffEditor(accessor: ServicesAccessor): ICodeEditor { +function getOuterEditorFromDiffEditor(accessor: ServicesAccessor): ICodeEditor | null { const diffEditors = accessor.get(ICodeEditorService).listDiffEditors(); for (const diffEditor of diffEditors) { @@ -203,11 +203,15 @@ class DirtyDiffWidget extends PeekViewWidget { this._applyTheme(themeService.getTheme()); this.contextKeyService = contextKeyService.createScoped(); - this.contextKeyService.createKey('originalResourceScheme', this.model.original.uri.scheme); + this.contextKeyService.createKey('originalResourceScheme', this.model.original!.uri.scheme); this.menu = menuService.createMenu(MenuId.SCMChangeContext, this.contextKeyService); this.create(); - this.title = basename(editor.getModel().uri.fsPath); + if (editor.hasModel()) { + this.title = basename(editor.getModel().uri.fsPath); + } else { + this.title = ''; + } this.setTitle(this.title); model.onDidChange(this.renderTitle, this, this._disposables); @@ -230,7 +234,7 @@ class DirtyDiffWidget extends PeekViewWidget { // non-side-by-side diff still hasn't created the view zones onFirstDiffUpdate(() => setTimeout(() => this.revealChange(change), 0)); - this.diffEditor.setModel(this.model); + this.diffEditor.setModel(this.model as IDiffEditorModel); const position = new Position(getModifiedEndLineNumber(change), 1); @@ -245,7 +249,7 @@ class DirtyDiffWidget extends PeekViewWidget { const changeTypeColor = getChangeTypeColor(this.themeService.getTheme(), changeType); this.style({ frameColor: changeTypeColor, arrowColor: changeTypeColor }); - this._actionbarWidget.context = [this.model.modified.uri, this.model.changes, index]; + this._actionbarWidget.context = [this.model.modified!.uri, this.model.changes, index]; this.show(position, height); this.editor.focus(); } @@ -290,9 +294,9 @@ class DirtyDiffWidget extends PeekViewWidget { }; } - getActionItem(action: IAction): IActionItem { + getActionItem(action: IAction): IActionItem | null { if (!(action instanceof MenuItemAction)) { - return undefined; + return null; } return new DiffMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService); @@ -454,7 +458,7 @@ export class MoveToPreviousChangeAction extends EditorAction { run(accessor: ServicesAccessor, editor: ICodeEditor): void { const outerEditor = getOuterEditorFromDiffEditor(accessor); - if (!outerEditor) { + if (!outerEditor || !outerEditor.hasModel()) { return; } @@ -467,7 +471,7 @@ export class MoveToPreviousChangeAction extends EditorAction { const lineNumber = outerEditor.getPosition().lineNumber; const model = controller.modelRegistry.getModel(outerEditor.getModel()); - if (model.changes.length === 0) { + if (!model || model.changes.length === 0) { return; } @@ -496,7 +500,7 @@ export class MoveToNextChangeAction extends EditorAction { run(accessor: ServicesAccessor, editor: ICodeEditor): void { const outerEditor = getOuterEditorFromDiffEditor(accessor); - if (!outerEditor) { + if (!outerEditor || !outerEditor.hasModel()) { return; } @@ -509,7 +513,7 @@ export class MoveToNextChangeAction extends EditorAction { const lineNumber = outerEditor.getPosition().lineNumber; const model = controller.modelRegistry.getModel(outerEditor.getModel()); - if (model.changes.length === 0) { + if (!model || model.changes.length === 0) { return; } @@ -585,15 +589,18 @@ export class DirtyDiffController implements IEditorContribution { } canNavigate(): boolean { - return this.currentIndex === -1 || this.model.changes.length > 1; + return this.currentIndex === -1 || (!!this.model && this.model.changes.length > 1); } next(lineNumber?: number): void { if (!this.assertWidget()) { return; } + if (!this.widget || !this.model) { + return; + } - if (typeof lineNumber === 'number' || this.currentIndex === -1) { + if (this.editor.hasModel() && (typeof lineNumber === 'number' || this.currentIndex === -1)) { this.currentIndex = this.model.findNextClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber); } else { this.currentIndex = rot(this.currentIndex + 1, this.model.changes.length); @@ -606,8 +613,11 @@ export class DirtyDiffController implements IEditorContribution { if (!this.assertWidget()) { return; } + if (!this.widget || !this.model) { + return; + } - if (typeof lineNumber === 'number' || this.currentIndex === -1) { + if (this.editor.hasModel() && (typeof lineNumber === 'number' || this.currentIndex === -1)) { this.currentIndex = this.model.findPreviousClosestChange(typeof lineNumber === 'number' ? lineNumber : this.editor.getPosition().lineNumber); } else { this.currentIndex = rot(this.currentIndex - 1, this.model.changes.length); @@ -627,7 +637,7 @@ export class DirtyDiffController implements IEditorContribution { } if (this.widget) { - if (this.model.changes.length === 0) { + if (!this.model || this.model.changes.length === 0) { this.close(); return false; } @@ -680,6 +690,9 @@ export class DirtyDiffController implements IEditorContribution { } private onDidModelChange(splices: ISplice[]): void { + if (!this.model) { + return; + } for (const splice of splices) { if (splice.start <= this.currentIndex) { if (this.currentIndex < splice.start + splice.deleteCount) { @@ -709,7 +722,9 @@ export class DirtyDiffController implements IEditorContribution { if (e.target.type !== MouseTargetType.GUTTER_LINE_DECORATIONS) { return; } - + if (!e.target.element) { + return; + } if (e.target.element.className.indexOf('dirty-diff-glyph') < 0) { return; } @@ -777,6 +792,9 @@ export class DirtyDiffController implements IEditorContribution { if (!this.modelRegistry) { return []; } + if (!this.editor.hasModel()) { + return []; + } const model = this.modelRegistry.getModel(this.editor.getModel()); @@ -841,12 +859,14 @@ class DirtyDiffDecorator { private deletedOptions: ModelDecorationOptions; private decorations: string[] = []; private disposables: IDisposable[] = []; + private editorModel: ITextModel | null; constructor( - private editorModel: ITextModel, + editorModel: ITextModel, private model: DirtyDiffModel, @IConfigurationService configurationService: IConfigurationService ) { + this.editorModel = editorModel; const decorations = configurationService.getValue('scm.diffDecorations'); const gutter = decorations === 'all' || decorations === 'gutter'; const overview = decorations === 'all' || decorations === 'overview'; @@ -860,6 +880,9 @@ class DirtyDiffDecorator { } private onDidChange(): void { + if (!this.editorModel) { + return; + } const decorations = this.model.changes.map((change) => { const changeType = getChangeType(change); const startLineNumber = change.modifiedStartLineNumber; @@ -932,12 +955,12 @@ function compareChanges(a: IChange, b: IChange): number { export class DirtyDiffModel { - private _originalModel: ITextModel; - get original(): ITextModel { return this._originalModel; } - get modified(): ITextModel { return this._editorModel; } + private _originalModel: ITextModel | null; + get original(): ITextModel | null { return this._originalModel; } + get modified(): ITextModel | null { return this._editorModel; } - private diffDelayer: ThrottledDelayer; - private _originalURIPromise: Promise; + private diffDelayer: ThrottledDelayer | null; + private _originalURIPromise?: Promise; private repositoryDisposables = new Set(); private originalModelDisposables: IDisposable[] = []; private disposables: IDisposable[] = []; @@ -950,15 +973,18 @@ export class DirtyDiffModel { return this._changes; } + private _editorModel: ITextModel | null; + constructor( - private _editorModel: ITextModel, + editorModel: ITextModel, @ISCMService private readonly scmService: ISCMService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, @ITextModelService private readonly textModelResolverService: ITextModelService ) { + this._editorModel = editorModel; this.diffDelayer = new ThrottledDelayer(200); - this.disposables.push(_editorModel.onDidChangeContent(() => this.triggerDiff())); + this.disposables.push(editorModel.onDidChangeContent(() => this.triggerDiff())); scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); scmService.repositories.forEach(r => this.onDidAddRepository(r)); @@ -989,7 +1015,7 @@ export class DirtyDiffModel { .trigger(() => this.diff()) .then((changes: IChange[]) => { if (!this._editorModel || this._editorModel.isDisposed() || !this._originalModel || this._originalModel.isDisposed()) { - return undefined; // disposed + return; // disposed } if (this._originalModel.getValueLength() === 0) { @@ -1005,7 +1031,7 @@ export class DirtyDiffModel { }); } - private diff(): Promise { + private diff(): Promise { return this.getOriginalURIPromise().then(originalURI => { if (!this._editorModel || this._editorModel.isDisposed() || !originalURI) { return Promise.resolve([]); // disposed @@ -1019,7 +1045,7 @@ export class DirtyDiffModel { }); } - private getOriginalURIPromise(): Promise { + private getOriginalURIPromise(): Promise { if (this._originalURIPromise) { return this._originalURIPromise; } @@ -1056,8 +1082,8 @@ export class DirtyDiffModel { }); }); - return always(this._originalURIPromise, () => { - this._originalURIPromise = null; + return always(this._originalURIPromise!, () => { + this._originalURIPromise = undefined; }); } @@ -1148,7 +1174,7 @@ export class DirtyDiffWorkbenchController implements ext.IWorkbenchContribution, @IConfigurationService private readonly configurationService: IConfigurationService ) { this.stylesheet = createStyleSheet(); - this.disposables.push(toDisposable(() => this.stylesheet.parentElement.removeChild(this.stylesheet))); + this.disposables.push(toDisposable(() => this.stylesheet.parentElement!.removeChild(this.stylesheet))); const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorations')); onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); @@ -1219,7 +1245,7 @@ export class DirtyDiffWorkbenchController implements ext.IWorkbenchContribution, }) // remove nulls and duplicates - .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1); + .filter((m, i, a) => !!m && !!m.uri && a.indexOf(m, i + 1) === -1) as ITextModel[]; const newModels = models.filter(o => this.models.every(m => o !== m)); const oldModels = this.models.filter(m => models.every(o => o !== m)); From 1904cd8d84c5aa615cb7730fd367d3ffae1fd0db Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 16 Jan 2019 17:54:04 -0800 Subject: [PATCH 0319/1066] Prototype auto fixable quick fixes Part of #62110 - Adds a new field `canAutoApply` to code actions. This field indicates that a code action can be applied without additional user input. For quick fixes, this should be set if the fix properly addresses the error - Enable auto fixes for TS spelling errors - Added a `editor.action.autoFix` command to triggers auto fixes at the current cursor position --- .../src/features/quickFix.ts | 9 ++++-- src/vs/editor/common/modes.ts | 1 + .../editor/contrib/codeAction/codeAction.ts | 11 ++++++-- .../contrib/codeAction/codeActionCommands.ts | 28 +++++++++++++++++++ .../codeAction/codeActionContributions.ts | 3 +- .../contrib/codeAction/codeActionTrigger.ts | 1 + src/vs/monaco.d.ts | 1 + src/vs/vscode.proposed.d.ts | 13 +++++++++ src/vs/workbench/api/node/extHost.protocol.ts | 1 + .../api/node/extHostLanguageFeatures.ts | 3 +- 10 files changed, 64 insertions(+), 7 deletions(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index f06706b7a41..0b4fa587ec0 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -240,7 +240,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { const results = new CodeActionSet(); for (const tsCodeFix of response.body) { - this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix); + this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction); } return results.values; } @@ -250,7 +250,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeAction + tsAction: Proto.CodeFixAction ): CodeActionSet { results.addAction(this.getSingleFixForTsCodeAction(diagnostic, tsAction)); this.addFixAllForTsCodeAction(results, document, file, diagnostic, tsAction as Proto.CodeFixAction); @@ -259,7 +259,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { private getSingleFixForTsCodeAction( diagnostic: vscode.Diagnostic, - tsAction: Proto.CodeAction + tsAction: Proto.CodeFixAction ): vscode.CodeAction { const codeAction = new vscode.CodeAction(tsAction.description, vscode.CodeActionKind.QuickFix); codeAction.edit = getEditForCodeAction(this.client, tsAction); @@ -269,6 +269,9 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { arguments: [tsAction], title: '' }; + if (tsAction.fixName === 'spelling') { + codeAction.canAutoApply = true; + } return codeAction; } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 55581b24c79..80eb6e5d057 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -516,6 +516,7 @@ export interface CodeAction { edit?: WorkspaceEdit; diagnostics?: IMarkerData[]; kind?: string; + canAutoApply?: boolean; } /** diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index ee8ecbb9048..8d012d07be3 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -15,7 +15,12 @@ import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTr import { IModelService } from 'vs/editor/common/services/modelService'; import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger'; -export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Selection, trigger?: CodeActionTrigger, token: CancellationToken = CancellationToken.None): Promise { +export function getCodeActions( + model: ITextModel, + rangeOrSelection: Range | Selection, + trigger?: CodeActionTrigger, + token: CancellationToken = CancellationToken.None +): Promise { const codeActionContext: CodeActionContext = { only: trigger && trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined, trigger: trigger && trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic @@ -65,7 +70,9 @@ export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Sele } function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean { - return action && isValidActionKind(filter, action.kind); + return action + && isValidActionKind(filter, action.kind) + && (filter && filter.autoFixesOnly ? !!action.canAutoApply : true); } function isValidActionKind(filter: CodeActionFilter | undefined, kind: string | undefined): boolean { diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 14acb6ee5b6..072d6b4ea58 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -358,3 +358,31 @@ export class OrganizeImportsAction extends EditorAction { CodeActionAutoApply.IfSingle); } } + +export class AutoFixAction extends EditorAction { + + static readonly Id = 'editor.action.autoFix'; + + constructor() { + super({ + id: AutoFixAction.Id, + label: nls.localize('autoFix.label', "Auto Fix"), + alias: 'Auto Fix', + precondition: ContextKeyExpr.and( + EditorContextKeys.writable, + contextKeyForSupportedActions(CodeActionKind.QuickFix)), + kbOpts: { + kbExpr: EditorContextKeys.editorTextFocus, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_DOT, + weight: KeybindingWeight.EditorContrib + } + }); + } + + public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { + return showCodeActionsForEditorSelection(editor, + nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), + { kind: CodeActionKind.QuickFix, autoFixesOnly: true }, + CodeActionAutoApply.IfSingle); + } +} diff --git a/src/vs/editor/contrib/codeAction/codeActionContributions.ts b/src/vs/editor/contrib/codeAction/codeActionContributions.ts index 4f7315db0f8..7a022b495b4 100644 --- a/src/vs/editor/contrib/codeAction/codeActionContributions.ts +++ b/src/vs/editor/contrib/codeAction/codeActionContributions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; +import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction, AutoFixAction } from 'vs/editor/contrib/codeAction/codeActionCommands'; registerEditorContribution(QuickFixController); @@ -12,4 +12,5 @@ registerEditorAction(QuickFixAction); registerEditorAction(RefactorAction); registerEditorAction(SourceAction); registerEditorAction(OrganizeImportsAction); +registerEditorAction(AutoFixAction); registerEditorCommand(new CodeActionCommand()); diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index b8ed8cde8fc..ad1bbfe85b1 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -32,6 +32,7 @@ export const enum CodeActionAutoApply { export interface CodeActionFilter { readonly kind?: CodeActionKind; readonly includeSourceActions?: boolean; + readonly autoFixesOnly?: boolean; } export interface CodeActionTrigger { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 4ec47e159d8..0cd4c9504fd 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4848,6 +4848,7 @@ declare namespace monaco.languages { edit?: WorkspaceEdit; diagnostics?: editor.IMarkerData[]; kind?: string; + canAutoApply?: boolean; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index edbf4b240fa..f659d20660c 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1098,4 +1098,17 @@ declare module 'vscode' { readonly activeSignatureHelp?: SignatureHelp; } //#endregion + + //#region CodeAction.canAutoApply - mjbvz + export interface CodeAction { + /** + * If the action can be safely automatically applied without the user selecting it from a list. + * + * Set this on quick fixes to indicate that the fix properly addresses the underlying error. + */ + canAutoApply?: boolean; + } + //#endregion + + } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 08baa733711..7ecd69fcb54 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -864,6 +864,7 @@ export interface CodeActionDto { diagnostics?: IMarkerData[]; command?: modes.Command; kind?: string; + canAutoApply?: boolean; } export interface ExtHostLanguageFeaturesShape { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 0801f8f05d9..df6512c9b6b 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -342,7 +342,8 @@ class CodeActionAdapter { command: candidate.command && this._commands.toInternal(candidate.command), diagnostics: candidate.diagnostics && candidate.diagnostics.map(typeConvert.Diagnostic.from), edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit), - kind: candidate.kind && candidate.kind.value + kind: candidate.kind && candidate.kind.value, + canAutoApply: candidate.canAutoApply, }); } } From 926cfe1c4ac6a438f8fa3c4f164219c7c4bf17b5 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Jan 2019 18:36:11 -0800 Subject: [PATCH 0320/1066] Update css language service for #66519 and #66637 --- extensions/css-language-features/server/package.json | 2 +- extensions/css-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 70a53550e66..3e87558befb 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.4", + "vscode-css-languageservice": "^3.0.13-next.6", "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 d8f8904133f..813bbb333c6 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.4: - version "3.0.13-next.4" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.4.tgz#3ff1a0fefe7b47c698fd48baf05dcea2b709b99d" - integrity sha512-Ji7YpGyGkUZF3KSz1da3dVuKkdGujeDrHE73gRfGxDxb2yE00ifPFueRf1DMXVBSQ4rCeVWzC4gxEhnnLJEc2w== +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== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 5afc8d51f90bb16829f62e0fb4f7c8b6f4707db1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 09:50:16 +0100 Subject: [PATCH 0321/1066] tree options: indent fixes #66605 --- src/vs/base/browser/ui/tree/abstractTree.ts | 39 +++++++++++++++++--- src/vs/base/browser/ui/tree/asyncDataTree.ts | 10 ++++- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b0c1d689ebc..2118d5e87fd 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -163,18 +163,25 @@ interface ITreeListTemplateData { templateData: T; } +interface ITreeRendererOptions { + readonly indent?: number; +} + class TreeRenderer implements IListRenderer, ITreeListTemplateData> { readonly templateId: string; private renderedElements = new Map>(); private renderedNodes = new Map, ITreeListTemplateData>(); + private indent = 12; private disposables: IDisposable[] = []; constructor( private renderer: ITreeRenderer, - onDidChangeCollapseState: Event> + onDidChangeCollapseState: Event>, + options: ITreeRendererOptions = {} ) { this.templateId = renderer.templateId; + this.updateOptions(options); Event.map(onDidChangeCollapseState, e => e.node)(this.onDidChangeNodeTwistieState, this, this.disposables); @@ -183,6 +190,15 @@ class TreeRenderer implements IListRenderer { + templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; + }); + } + renderTemplate(container: HTMLElement): ITreeListTemplateData { const el = append(container, $('.monaco-tl-row')); const twistie = append(el, $('.monaco-tl-twistie')); @@ -196,7 +212,7 @@ class TreeRenderer implements IListRenderer(event: IListContextMenuEvent extends IListOptions { +export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { + +} + +export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { readonly collapseByDefault?: boolean; // defaults to false readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; @@ -286,6 +306,7 @@ export interface IAbstractTreeOptions extends IListOption export abstract class AbstractTree implements IDisposable { private view: List>; + private renderers: TreeRenderer[]; protected model: ITreeModel; protected disposables: IDisposable[] = []; @@ -318,10 +339,10 @@ export abstract class AbstractTree implements IDisposable const treeDelegate = new ComposedTreeDelegate>(delegate); const onDidChangeCollapseStateRelay = new Relay>(); - const treeRenderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event)); - this.disposables.push(...treeRenderers); + this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, options)); + this.disposables.push(...this.renderers); - this.view = new List(container, treeDelegate, treeRenderers, asListOptions(() => this.model, options)); + this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, options)); this.model = this.createModel(this.view, options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; @@ -339,6 +360,12 @@ export abstract class AbstractTree implements IDisposable } } + updateOptions(options: IAbstractTreeOptionsUpdate = {}): void { + for (const renderer of this.renderers) { + renderer.updateOptions(options); + } + } + // Widget getHTMLElement(): HTMLElement { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 39caf7f0d1c..405c4161d54 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ComposedTreeDelegate, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; +import { ComposedTreeDelegate, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; import { IListVirtualDelegate, IIdentityProvider, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; import { ITreeElement, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeSorter, ICollapseStateChangeEvent, IAsyncDataSource, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; @@ -228,7 +228,9 @@ function asTreeElement(node: IAsyncDataTreeNode): ITreeEle }; } -export interface IAsyncDataTreeOptions extends IAbstractTreeOptions { +export interface IAsyncDataTreeOptionsUpdate extends IAbstractTreeOptionsUpdate { } + +export interface IAsyncDataTreeOptions extends IAsyncDataTreeOptionsUpdate, IAbstractTreeOptions { identityProvider?: IIdentityProvider; sorter?: ITreeSorter; autoExpandSingleChildren?: boolean; @@ -309,6 +311,10 @@ export class AsyncDataTree implements IDisposable this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); } + updateOptions(options: IAsyncDataTreeOptionsUpdate = {}): void { + this.tree.updateOptions(options); + } + // Widget getHTMLElement(): HTMLElement { From 9123a3a5c436ccb4eb9bea3bacfb31a488090023 Mon Sep 17 00:00:00 2001 From: Christopher Leidigh Date: Thu, 17 Jan 2019 03:55:52 -0500 Subject: [PATCH 0322/1066] GoToLine: Screenreader output line number, handles #66245, #65219 (#66249) * GoToLine: Screenreader output line number, handle #66245, #65219 * Tweak value checking, logic * example WIP 1 * Add shortcut for example 4 - WIP spaghetti * Add examples in GoToAction2 WIP * Rewind to Example 1 PR * Address @bpasero comments * Mirror changes in standalone editor --- .../standalone/browser/quickOpen/gotoLine.ts | 9 ++++++++- .../parts/quickopen/browser/gotoLineHandler.ts | 18 ++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 0a4135b4087..74c34fb5cdc 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -86,7 +86,14 @@ export class GotoLineEntry extends QuickOpenEntry { } public getAriaLabel(): string { - return nls.localize('gotoLineAriaLabel', "Go to line {0}", this._parseResult.label); + + if (this.editor) { + const currentLine = this.editor.getPosition().lineNumber; + // Output verbose current line / go to line label + return nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {0}.", currentLine, this._parseResult.label); + } + + return nls.localize('cannotRunGotoLine', "Open a text file first to go to a line."); } public run(mode: Mode, context: IContext): boolean { diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index 16718fdef5d..857dcb220c6 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -91,12 +91,15 @@ class GotoLineEntry extends EditorQuickOpenEntry { // Inform user about valid range if input is invalid const maxLineNumber = this.getMaxLineNumber(); + if (this.invalidRange(maxLineNumber)) { + const line = this.editorService.activeTextEditorWidget.getPosition().lineNumber; + if (maxLineNumber > 0) { - return nls.localize('gotoLineLabelEmptyWithLimit', "Type a line number between 1 and {0} to navigate to", maxLineNumber); + return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", line, maxLineNumber); } - return nls.localize('gotoLineLabelEmpty', "Type a line number to navigate to"); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", line); } // Input valid, indicate action @@ -213,7 +216,14 @@ export class GotoLineHandler extends QuickOpenHandler { } getAriaLabel(): string { - return nls.localize('gotoLineHandlerAriaLabel', "Type a line number to navigate to."); + + if (this.editorService.activeTextEditorWidget) { + const currentLine = this.editorService.activeTextEditorWidget.getPosition().lineNumber; + // Output verbose current line / go to line label + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + } + + return nls.localize('cannotRunGotoLine', "Open a text file first to go to a line."); } getResults(searchValue: string, token: CancellationToken): Promise { @@ -231,7 +241,7 @@ export class GotoLineHandler extends QuickOpenHandler { canRun(): boolean | string { const canRun = !!this.editorService.activeTextEditorWidget; - return canRun ? true : nls.localize('cannotRunGotoLine', "Open a text file first to go to a line"); + return canRun ? true : nls.localize('cannotRunGotoLine', "Open a text file first to go to a line."); } decorateOutline(range: IRange, editor: IEditor, group: IEditorGroup): void { From 2cdf1d239873ca7042e0a1e36b01ead9398a81f7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 10:01:34 +0100 Subject: [PATCH 0323/1066] :lipstick: --- .../standalone/browser/quickOpen/gotoLine.ts | 56 ++++++++----------- .../quickopen/browser/gotoLineHandler.ts | 11 ++-- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 74c34fb5cdc..7c55710e9a2 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -25,8 +25,7 @@ interface ParseResult { } export class GotoLineEntry extends QuickOpenEntry { - - private _parseResult: ParseResult; + private parseResult: ParseResult; private decorator: IDecorator; private editor: editorCommon.IEditor; @@ -35,14 +34,12 @@ export class GotoLineEntry extends QuickOpenEntry { this.editor = editor; this.decorator = decorator; - this._parseResult = this._parseInput(line); + this.parseResult = this.parseInput(line); } - - private _parseInput(line: string): ParseResult { - - let numbers = line.split(',').map(part => parseInt(part, 10)).filter(part => !isNaN(part)), - position: Position; + private parseInput(line: string): ParseResult { + const numbers = line.split(',').map(part => parseInt(part, 10)).filter(part => !isNaN(part)); + let position: Position; if (numbers.length === 0) { position = new Position(-1, -1); @@ -59,8 +56,8 @@ export class GotoLineEntry extends QuickOpenEntry { model = (this.editor).getModel().modified; } - let isValid = model.validatePosition(position).equals(position), - label: string; + const isValid = model.validatePosition(position).equals(position); + let label: string; if (isValid) { if (position.column && position.column > 1) { @@ -81,22 +78,17 @@ export class GotoLineEntry extends QuickOpenEntry { }; } - public getLabel(): string { - return this._parseResult.label; + getLabel(): string { + return this.parseResult.label; } - public getAriaLabel(): string { + getAriaLabel(): string { + const currentLine = this.editor.getPosition().lineNumber; - if (this.editor) { - const currentLine = this.editor.getPosition().lineNumber; - // Output verbose current line / go to line label - return nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {0}.", currentLine, this._parseResult.label); - } - - return nls.localize('cannotRunGotoLine', "Open a text file first to go to a line."); + return nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {0}.", currentLine, this.parseResult.label); } - public run(mode: Mode, context: IContext): boolean { + run(mode: Mode, context: IContext): boolean { if (mode === Mode.OPEN) { return this.runOpen(); } @@ -104,15 +96,15 @@ export class GotoLineEntry extends QuickOpenEntry { return this.runPreview(); } - public runOpen(): boolean { + runOpen(): boolean { // No-op if range is not valid - if (!this._parseResult.isValid) { + if (!this.parseResult.isValid) { return false; } // Apply selection and focus - let range = this.toSelection(); + const range = this.toSelection(); (this.editor).setSelection(range); (this.editor).revealRangeInCenter(range, editorCommon.ScrollType.Smooth); this.editor.focus(); @@ -120,16 +112,16 @@ export class GotoLineEntry extends QuickOpenEntry { return true; } - public runPreview(): boolean { + runPreview(): boolean { // No-op if range is not valid - if (!this._parseResult.isValid) { + if (!this.parseResult.isValid) { this.decorator.clearDecorations(); return false; } // Select Line Position - let range = this.toSelection(); + const range = this.toSelection(); this.editor.revealRangeInCenter(range, editorCommon.ScrollType.Smooth); // Decorate if possible @@ -140,10 +132,10 @@ export class GotoLineEntry extends QuickOpenEntry { private toSelection(): Range { return new Range( - this._parseResult.position.lineNumber, - this._parseResult.position.column, - this._parseResult.position.lineNumber, - this._parseResult.position.column + this.parseResult.position.lineNumber, + this.parseResult.position.column, + this.parseResult.position.lineNumber, + this.parseResult.position.column ); } } @@ -165,7 +157,7 @@ export class GotoLineAction extends BaseEditorQuickOpenAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + run(accessor: ServicesAccessor, editor: ICodeEditor): void { this._show(this.getController(editor), { getModel: (value: string): QuickOpenModel => { return new QuickOpenModel([new GotoLineEntry(value, editor, this.getController(editor))]); diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index 857dcb220c6..a29d34e5c9c 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -93,17 +93,17 @@ class GotoLineEntry extends EditorQuickOpenEntry { const maxLineNumber = this.getMaxLineNumber(); if (this.invalidRange(maxLineNumber)) { - const line = this.editorService.activeTextEditorWidget.getPosition().lineNumber; + const currentLine = this.editorService.activeTextEditorWidget.getPosition().lineNumber; if (maxLineNumber > 0) { - return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", line, maxLineNumber); + return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber); } - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", line); + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); } // Input valid, indicate action - return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}", this.line); + return this.column ? nls.localize('gotoLineColumnLabel', "Go to line {0} and character {1}.", this.line, this.column) : nls.localize('gotoLineLabel', "Go to line {0}.", this.line); } private invalidRange(maxLineNumber: number = this.getMaxLineNumber()): boolean { @@ -216,10 +216,9 @@ export class GotoLineHandler extends QuickOpenHandler { } getAriaLabel(): string { - if (this.editorService.activeTextEditorWidget) { const currentLine = this.editorService.activeTextEditorWidget.getPosition().lineNumber; - // Output verbose current line / go to line label + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); } From 0ad67db153f37a552d1098166d0c036d6445bd46 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 10:01:42 +0100 Subject: [PATCH 0324/1066] change default tree indent to 8 --- src/vs/base/browser/ui/tree/abstractTree.ts | 5 ++--- .../parts/files/electron-browser/media/explorerviewlet.css | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 2118d5e87fd..e8e7cdeeea4 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -172,7 +172,7 @@ class TreeRenderer implements IListRenderer>(); private renderedNodes = new Map, ITreeListTemplateData>(); - private indent = 12; + private indent: number; private disposables: IDisposable[] = []; constructor( @@ -191,8 +191,7 @@ class TreeRenderer implements IListRenderer { templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; 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 a38a2026ee5..06bcd022031 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -14,6 +14,10 @@ height: 100%; } +.explorer-folders-view .monaco-list-row { + padding-left: 4px; /* align top level twistie with `Explorer` title label */ +} + .explorer-viewlet .monaco-list.highlight .explorer-item:not(.explorer-item-edited), .explorer-viewlet .monaco-list.highlight .monaco-tl-twistie { opacity: 0.3; From 2540182ac99d20927b8e91f4d9d0a3135966892d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 16 Jan 2019 12:57:44 +0100 Subject: [PATCH 0325/1066] Fix #66598 --- .../api/browser/viewsExtensionPoint.ts | 118 ++++++++++-------- src/vs/workbench/common/views.ts | 2 +- 2 files changed, 70 insertions(+), 50 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 4917da86ad2..99ea9a0a611 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -6,7 +6,7 @@ import { localize } from 'vs/nls'; import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ViewContainer, ViewsRegistry, ICustomViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -82,11 +82,12 @@ const viewsContribution: IJSONSchema = { } }; - -const viewsExtensionPoint: IExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }> = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IUserFriendlyViewDescriptor[] }>({ +type ViewExtensionPointType = { [loc: string]: IUserFriendlyViewDescriptor[] }; +const viewsExtensionPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'views', deps: [viewsContainersExtensionPoint], - jsonSchema: viewsContribution + jsonSchema: viewsContribution, + isDynamic: true }); class ViewsContainersExtensionHandler implements IWorkbenchContribution { @@ -101,54 +102,74 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } private handleAndRegisterCustomViews() { - viewsExtensionPoint.setHandler(extensions => { - for (let extension of extensions) { - const { value, collector } = extension; - - forEach(value, entry => { - if (!this.isValidViewDescriptors(entry.value, collector)) { - return; - } - - let container = this.getViewContainer(entry.key); - if (!container) { - collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key)); - container = this.viewContainersRegistry.get(EXPLORER); - } - const registeredViews = ViewsRegistry.getViews(container); - const viewIds: string[] = []; - const viewDescriptors = coalesce(entry.value.map((item, index) => { - // validate - if (viewIds.indexOf(item.id) !== -1) { - collector.error(localize('duplicateView1', "Cannot register multiple views with same id `{0}` in the view container `{1}`", item.id, container.id)); - return null; - } - if (registeredViews.some(v => v.id === item.id)) { - collector.error(localize('duplicateView2', "A view with id `{0}` is already registered in the view container `{1}`", item.id, container.id)); - return null; - } - - const viewDescriptor = { - id: item.id, - name: item.name, - ctor: CustomTreeViewPanel, - container, - when: ContextKeyExpr.deserialize(item.when), - canToggleVisibility: true, - collapsed: this.showCollapsed(container), - treeView: this.instantiationService.createInstance(CustomTreeView, item.id, container), - order: ExtensionIdentifier.equals(extension.description.identifier, container.extensionId) ? index + 1 : undefined - }; - - viewIds.push(viewDescriptor.id); - return viewDescriptor; - })); - ViewsRegistry.registerViews(viewDescriptors); - }); + viewsExtensionPoint.setHandler((extensions, { added, removed }) => { + if (removed.length) { + this.removeViews(removed); + } + if (added.length) { + this.addViews(added); } }); } + private addViews(extensions: IExtensionPointUser[]): void { + for (const extension of extensions) { + const { value, collector } = extension; + + forEach(value, entry => { + if (!this.isValidViewDescriptors(entry.value, collector)) { + return; + } + + let container = this.getViewContainer(entry.key); + if (!container) { + collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key)); + container = this.viewContainersRegistry.get(EXPLORER); + } + const registeredViews = ViewsRegistry.getViews(container); + const viewIds: string[] = []; + const viewDescriptors = coalesce(entry.value.map((item, index) => { + // validate + if (viewIds.indexOf(item.id) !== -1) { + collector.error(localize('duplicateView1', "Cannot register multiple views with same id `{0}` in the view container `{1}`", item.id, container.id)); + return null; + } + if (registeredViews.some(v => v.id === item.id)) { + collector.error(localize('duplicateView2', "A view with id `{0}` is already registered in the view container `{1}`", item.id, container.id)); + return null; + } + + const viewDescriptor = { + id: item.id, + name: item.name, + ctor: CustomTreeViewPanel, + container, + when: ContextKeyExpr.deserialize(item.when), + canToggleVisibility: true, + 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 + }; + + viewIds.push(viewDescriptor.id); + return viewDescriptor; + })); + ViewsRegistry.registerViews(viewDescriptors); + }); + } + } + + private removeViews(extensions: IExtensionPointUser[]): void { + const removedExtensions: Set = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); + for (const viewContainer of this.viewContainersRegistry.all) { + const removedViews = ViewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId))); + if (removedViews.length) { + ViewsRegistry.deregisterViews(removedViews.map(v => v.id), viewContainer); + } + } + } + private isValidViewDescriptors(viewDescriptors: IUserFriendlyViewDescriptor[], collector: ExtensionMessageCollector): boolean { if (!Array.isArray(viewDescriptors)) { collector.error(localize('requirearray', "views must be an array")); @@ -173,7 +194,6 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { return true; } - private getViewContainer(value: string): ViewContainer { switch (value) { case 'explorer': return this.viewContainersRegistry.get(EXPLORER); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index afb29fd530c..7b36e2170b5 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -297,7 +297,7 @@ export interface IRevealOptions { export interface ICustomViewDescriptor extends IViewDescriptor { readonly treeView: ITreeView; - + readonly extensionId: ExtensionIdentifier; } export type TreeViewItemHandleArg = { From 75659a85b17339b94c0d8f0992ee612e7c44b0cd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 10:43:03 +0100 Subject: [PATCH 0326/1066] Expose some more commands in the 'Go' menu (fixes #66395) --- .../bracketMatching/bracketMatching.ts | 11 ++++++++ .../goToDefinition/goToDefinitionCommands.ts | 12 ++++----- src/vs/editor/contrib/gotoError/gotoError.ts | 20 ++++++++++++++ .../parts/editor/editor.contribution.ts | 18 ++++++++++--- .../fileActions.contribution.ts | 2 +- .../browser/quickopen.contribution.ts | 10 +++---- .../electron-browser/dirtydiffDecorator.ts | 21 ++++++++++++++- .../electron-browser/search.contribution.ts | 4 +-- .../history/electron-browser/history.ts | 27 +++++++++++-------- 9 files changed, 95 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts index 36848e40645..62dea606561 100644 --- a/src/vs/editor/contrib/bracketMatching/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/bracketMatching.ts @@ -21,6 +21,7 @@ import { editorBracketMatchBackground, editorBracketMatchBorder } from 'vs/edito import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/common/themeService'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); @@ -322,3 +323,13 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-editor .bracket-match { border: 1px solid ${bracketMatchBorder}; }`); } }); + +// Go to menu +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '5_infile_nav', + command: { + id: 'editor.action.jumpToBracket', + title: nls.localize({ key: 'miGoToBracket', comment: ['&& denotes a mnemonic'] }, "Go to &&Bracket") + }, + order: 2 +}); \ No newline at end of file diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 47af90c355f..52648d04694 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -456,28 +456,28 @@ registerEditorAction(PeekTypeDefinitionAction); // Go to menu MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '4_symbol_nav', command: { id: 'editor.action.goToDeclaration', title: nls.localize({ key: 'miGotoDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Definition") }, - order: 4 + order: 2 }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '4_symbol_nav', command: { id: 'editor.action.goToTypeDefinition', title: nls.localize({ key: 'miGotoTypeDefinition', comment: ['&& denotes a mnemonic'] }, "Go to &&Type Definition") }, - order: 5 + order: 3 }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '4_symbol_nav', command: { id: 'editor.action.goToImplementation', title: nls.localize({ key: 'miGotoImplementation', comment: ['&& denotes a mnemonic'] }, "Go to &&Implementation") }, - order: 6 + order: 4 }); diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index e8db7961ddb..64f7b1fe292 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -23,6 +23,7 @@ import { compare } from 'vs/base/common/strings'; import { binarySearch } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; class MarkerModel { @@ -468,3 +469,22 @@ registerEditorCommand(new MarkerCommand({ secondary: [KeyMod.Shift | KeyCode.Escape] } })); + +// Go to menu +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '6_problem_nav', + command: { + id: 'editor.action.marker.next', + title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '6_problem_nav', + command: { + id: 'editor.action.marker.prev', + title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem") + }, + order: 2 +}); \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2b93fed352c..6d34336e1ab 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -740,7 +740,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarLayoutMenu, { // Forward/Back MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '1_fwd_back', + group: '1_history_nav', command: { id: 'workbench.action.navigateBack', title: nls.localize({ key: 'miBack', comment: ['&& denotes a mnemonic'] }, "&&Back"), @@ -750,7 +750,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '1_fwd_back', + group: '1_history_nav', command: { id: 'workbench.action.navigateForward', title: nls.localize({ key: 'miForward', comment: ['&& denotes a mnemonic'] }, "&&Forward"), @@ -759,6 +759,16 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { order: 2 }); +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '1_history_nav', + command: { + id: 'workbench.action.navigateToLastEditLocation', + title: nls.localize({ key: 'miLastEditLocation', comment: ['&& denotes a mnemonic'] }, "&&Last Edit Location"), + precondition: ContextKeyExpr.has('canNavigateToLastEditLocation') + }, + order: 3 +}); + // Switch Editor MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { group: '1_any', @@ -797,7 +807,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchEditorMenu, { }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch', + group: '2_editor_nav', title: nls.localize({ key: 'miSwitchEditor', comment: ['&& denotes a mnemonic'] }, "Switch &&Editor"), submenu: MenuId.MenubarSwitchEditorMenu, order: 1 @@ -904,7 +914,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarSwitchGroupMenu, { }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: '2_switch', + group: '2_editor_nav', title: nls.localize({ key: 'miSwitchGroup', comment: ['&& denotes a mnemonic'] }, "Switch &&Group"), submenu: MenuId.MenubarSwitchGroupMenu, order: 2 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 cc6fe49a164..e00908e44a2 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -583,7 +583,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { // Go to menu MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '3_global_nav', command: { id: 'workbench.action.quickOpen', title: nls.localize({ key: 'miGotoFile', comment: ['&& denotes a mnemonic'] }, "Go to &&File...") diff --git a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts index 0f8a6b8e0e8..e6ffdb918bb 100644 --- a/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts +++ b/src/vs/workbench/parts/quickopen/browser/quickopen.contribution.ts @@ -167,19 +167,19 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { // Go to menu MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '4_symbol_nav', command: { id: 'workbench.action.gotoSymbol', title: nls.localize({ key: 'miGotoSymbolInFile', comment: ['&& denotes a mnemonic'] }, "Go to &&Symbol in File...") }, - order: 2 + order: 1 }); MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '5_infile_nav', command: { id: 'workbench.action.gotoLine', - title: nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line...") + title: nls.localize({ key: 'miGotoLine', comment: ['&& denotes a mnemonic'] }, "Go to &&Line/Column...") }, - order: 7 + order: 1 }); \ No newline at end of file diff --git a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts index 1921703d8a9..a2683e8d6a8 100644 --- a/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts @@ -38,7 +38,7 @@ import { Action, IAction, ActionRunner } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation, IActionItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename } from 'vs/base/common/paths'; -import { MenuId, IMenuService, IMenu, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { MenuId, IMenuService, IMenu, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { MenuItemActionItem, fillInActionBarActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IChange, IEditorModel, ScrollType, IEditorContribution, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { OverviewRulerLane, ITextModel, IModelDecorationOptions } from 'vs/editor/common/model'; @@ -443,6 +443,25 @@ export class ShowNextChangeAction extends EditorAction { } registerEditorAction(ShowNextChangeAction); +// Go to menu +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '7_change_nav', + command: { + id: 'editor.action.dirtydiff.next', + title: nls.localize({ key: 'miGotoNextChange', comment: ['&& denotes a mnemonic'] }, "Next &&Change") + }, + order: 1 +}); + +MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { + group: '7_change_nav', + command: { + id: 'editor.action.dirtydiff.previous', + title: nls.localize({ key: 'miGotoPreviousChange', comment: ['&& denotes a mnemonic'] }, "Previous &&Change") + }, + order: 2 +}); + export class MoveToPreviousChangeAction extends EditorAction { constructor() { 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 f5b14fe336d..c8d7d8b7d75 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -714,10 +714,10 @@ MenuRegistry.appendMenuItem(MenuId.MenubarViewMenu, { // Go to menu MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { - group: 'z_go_to', + group: '3_global_nav', command: { id: 'workbench.action.showAllSymbols', title: nls.localize({ key: 'miGotoSymbolInWorkspace', comment: ['&& denotes a mnemonic'] }, "Go to Symbol in &&Workspace...") }, - order: 3 + order: 2 }); diff --git a/src/vs/workbench/services/history/electron-browser/history.ts b/src/vs/workbench/services/history/electron-browser/history.ts index c10a07ee558..ca31a8508d2 100644 --- a/src/vs/workbench/services/history/electron-browser/history.ts +++ b/src/vs/workbench/services/history/electron-browser/history.ts @@ -20,7 +20,7 @@ import { Event } from 'vs/base/common/event'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { getExcludes, ISearchConfiguration } from 'vs/platform/search/common/search'; import { IExpression } from 'vs/base/common/glob'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -127,6 +127,7 @@ export class HistoryService extends Disposable implements IHistoryService { private canNavigateBackContextKey: IContextKey; private canNavigateForwardContextKey: IContextKey; + private canNavigateToLastEditLocationContextKey: IContextKey; constructor( @IEditorService private readonly editorService: EditorServiceImpl, @@ -146,6 +147,7 @@ export class HistoryService extends Disposable implements IHistoryService { this.canNavigateBackContextKey = (new RawContextKey('canNavigateBack', false)).bindTo(this.contextKeyService); this.canNavigateForwardContextKey = (new RawContextKey('canNavigateForward', false)).bindTo(this.contextKeyService); + this.canNavigateToLastEditLocationContextKey = (new RawContextKey('canNavigateToLastEditLocation', false)).bindTo(this.contextKeyService); this.fileInputFactory = Registry.as(EditorInputExtensions.EditorInputFactories).getFileInputFactory(); @@ -216,17 +218,20 @@ export class HistoryService extends Disposable implements IHistoryService { // Track the last edit location by tracking model content change events // Use a debouncer to make sure to capture the correct cursor position // after the model content has changed. - this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => { - this.lastEditLocation = { input: activeEditor }; + this.activeEditorListeners.push(Event.debounce(activeTextEditorWidget.onDidChangeModelContent, (last, event) => event, 0)((event => this.rememberLastEditLocation(activeEditor, activeTextEditorWidget)))); + } + } - const position = activeTextEditorWidget.getPosition(); - if (position) { - this.lastEditLocation.selection = { - startLineNumber: position.lineNumber, - startColumn: position.column - }; - } - }))); + private rememberLastEditLocation(activeEditor: IEditorInput, activeTextEditorWidget: ICodeEditor): void { + this.lastEditLocation = { input: activeEditor }; + this.canNavigateToLastEditLocationContextKey.set(true); + + const position = activeTextEditorWidget.getPosition(); + if (position) { + this.lastEditLocation.selection = { + startLineNumber: position.lineNumber, + startColumn: position.column + }; } } From 6c2bc930fd17ec586dcb696ebe450beef19c6d4c Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 10:50:12 +0100 Subject: [PATCH 0327/1066] debug: open viewlet before attach / launch fixes #66617 --- .../parts/debug/electron-browser/debugService.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index b4130cdac63..2e17633e4a1 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -428,6 +428,12 @@ export class DebugService implements IDebugService { // this event doesn't go to extensions this._onWillNewSession.fire(session); + const openDebug = this.configurationService.getValue('debug').openDebug; + // Open debug viewlet based on the visibility of the side bar and openDebug setting. Do not open for 'run without debug' + if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.viewModel.firstSessionStart))) { + this.viewletService.openViewlet(VIEWLET_ID).then(undefined, errors.onUnexpectedError); + } + return this.launchOrAttachToSession(session).then(() => { // since the initialized response has arrived announce the new Session (including extensions) @@ -438,11 +444,6 @@ export class DebugService implements IDebugService { this.panelService.openPanel(REPL_ID, false); } - const openDebug = this.configurationService.getValue('debug').openDebug; - // Open debug viewlet based on the visibility of the side bar and openDebug setting. Do not open for 'run without debug' - if (!configuration.resolved.noDebug && (openDebug === 'openOnSessionStart' || (openDebug === 'openOnFirstSessionStart' && this.viewModel.firstSessionStart))) { - this.viewletService.openViewlet(VIEWLET_ID); - } this.viewModel.firstSessionStart = false; if (this.model.getSessions().length > 1) { From 041b5325e6e6d4f8a3edfb99c70a1f5475f4aa18 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 10:49:40 +0100 Subject: [PATCH 0328/1066] tips - be consistent with entries in "no folder" case --- src/vs/workbench/parts/watermark/electron-browser/watermark.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index db244eb6cb5..35f3f10cd70 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -87,8 +87,7 @@ const noFolderEntries = [ openFolderNonMacOnly, openFileOrFolderMacOnly, openRecent, - newUntitledFileMacOnly, - toggleTerminal + newUntitledFileMacOnly ]; const folderEntries = [ From 24b75dad90f8628edcccdd3070bdf84dbb751e00 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 11:05:51 +0100 Subject: [PATCH 0329/1066] workbench.tree.indent fixes #35447 --- src/vs/platform/list/browser/listService.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 3ae319b4e66..4bf1143f14f 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -113,6 +113,7 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; export const openModeSettingKey = 'workbench.list.openMode'; export const horizontalScrollingKey = 'workbench.tree.horizontalScrolling'; +const treeIndentKey = 'workbench.tree.indent'; function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean { return configurationService.getValue(multiSelectModifierSettingKey) === 'alt'; @@ -908,7 +909,8 @@ export class WorkbenchObjectTree, TFilterData = void> keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService) + ...toWorkbenchListOptions(options, configurationService, keybindingService), + indent: configurationService.getValue(treeIndentKey) }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -944,6 +946,10 @@ export class WorkbenchObjectTree, TFilterData = void> if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } + if (e.affectsConfiguration(treeIndentKey)) { + const indent = configurationService.getValue(treeIndentKey); + this.updateOptions({ indent }); + } }) ); } @@ -1055,7 +1061,8 @@ export class WorkbenchAsyncDataTree extends Async keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService) + ...toWorkbenchListOptions(options, configurationService, keybindingService), + indent: configurationService.getValue(treeIndentKey) }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -1091,6 +1098,10 @@ export class WorkbenchAsyncDataTree extends Async if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } + if (e.affectsConfiguration(treeIndentKey)) { + const indent = configurationService.getValue(treeIndentKey); + this.updateOptions({ indent }); + } }) ); } @@ -1137,6 +1148,11 @@ configurationRegistry.registerConfiguration({ 'type': 'boolean', 'default': false, 'description': localize('horizontalScrolling setting', "Controls whether trees support horizontal scrolling in the workbench.") + }, + [treeIndentKey]: { + 'type': 'number', + 'default': 8, + 'description': localize('tree indent setting', "Controls tree indentation in pixels.") } } }); From bbf3b04bfb6403ddf0f29ee567d38964f6c54df3 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 11:18:03 +0100 Subject: [PATCH 0330/1066] explroer: wasSelectedAndKeyboard --- .../parts/files/electron-browser/views/explorerView.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 f87a6a88c14..30e35834397 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -292,11 +292,11 @@ export class ExplorerView extends ViewletPanel { return; } const selection = e.elements; - const wasSelected = this.previousSelection.indexOf(selection[0]) >= 0; - this.previousSelection = selection; - // Do not react if the user is expanding selection. + // Do not react if the user is expanding selection via keyboard. // Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589. - if (selection.length === 1 && !wasSelected) { + const wasSelectedAndKeyboard = (e.browserEvent instanceof KeyboardEvent) && this.previousSelection.indexOf(selection[0]) >= 0; + this.previousSelection = selection; + if (selection.length === 1 && !wasSelectedAndKeyboard) { // Do not react if user is clicking on explorer items which are input placeholders if (!selection[0].name) { // Do not react if user is clicking on explorer items which are input placeholders From 6c570ec920f88b7a3ad659a14135da245caf13a2 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 11:24:23 +0100 Subject: [PATCH 0331/1066] WorkbenchDataTree indent --- src/vs/platform/list/browser/listService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 4bf1143f14f..4d7be7cd635 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -990,7 +990,8 @@ export class WorkbenchDataTree extends DataTree extends DataTree(treeIndentKey); + this.updateOptions({ indent }); + } }) ); } From 44f9a685d07ff8b7c5302d3c75e46eaf03ecd8b0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 11:24:53 +0100 Subject: [PATCH 0332/1066] Welcome: Improve "keyboard tips" visual presentation (fixes #64901) --- .../ui/keybindingLabel/keybindingLabel.ts | 17 +++++--- .../parts/quickopen/browser/quickOpenModel.ts | 3 +- .../electron-browser/extensionEditor.ts | 2 +- .../preferences/browser/keybindingWidgets.ts | 4 +- .../watermark/electron-browser/watermark.css | 19 --------- .../watermark/electron-browser/watermark.ts | 39 ++++++++----------- 6 files changed, 32 insertions(+), 52 deletions(-) diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts index ecc926434d6..707c56b25ee 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./keybindingLabel'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { equals } from 'vs/base/common/objects'; import { OperatingSystem } from 'vs/base/common/platform'; import { ResolvedKeybinding, ResolvedKeybindingPart } from 'vs/base/common/keyCodes'; import { UILabelProvider } from 'vs/base/common/keybindingLabels'; import * as dom from 'vs/base/browser/dom'; +import { localize } from 'vs/nls'; const $ = dom.$; @@ -26,14 +26,18 @@ export interface Matches { chordPart: PartMatches; } -export class KeybindingLabel implements IDisposable { +export interface KeybindingLabelOptions { + renderUnboundKeybindings: boolean; +} + +export class KeybindingLabel { private domNode: HTMLElement; private keybinding: ResolvedKeybinding; private matches: Matches; private didEverRender: boolean; - constructor(container: HTMLElement, private os: OperatingSystem) { + constructor(container: HTMLElement, private os: OperatingSystem, private options?: KeybindingLabelOptions) { this.domNode = dom.append(container, $('.monaco-keybinding')); this.didEverRender = false; container.appendChild(this.domNode); @@ -43,7 +47,7 @@ export class KeybindingLabel implements IDisposable { return this.domNode; } - set(keybinding: ResolvedKeybinding, matches: Matches) { + set(keybinding: ResolvedKeybinding, matches?: Matches) { if (this.didEverRender && this.keybinding === keybinding && KeybindingLabel.areSame(this.matches, matches)) { return; } @@ -66,6 +70,8 @@ export class KeybindingLabel implements IDisposable { this.renderPart(this.domNode, chordPart, this.matches ? this.matches.chordPart : null); } this.domNode.title = this.keybinding.getAriaLabel() || ''; + } else if (this.options && this.options.renderUnboundKeybindings) { + this.renderUnbound(this.domNode); } this.didEverRender = true; @@ -98,7 +104,8 @@ export class KeybindingLabel implements IDisposable { } } - dispose() { + private renderUnbound(parent: HTMLElement): void { + dom.append(parent, $('span.monaco-keybinding-key', undefined, localize('unbound', "Unbound"))); } private static areSame(a: Matches, b: Matches): boolean { diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index e2081d58429..5780dee88ca 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -474,7 +474,7 @@ class Renderer implements IRenderer { data.detail.set(entry.getDetail(), detailHighlights); // Keybinding - data.keybinding.set(entry.getKeybinding(), null); + data.keybinding.set(entry.getKeybinding()); } } @@ -484,7 +484,6 @@ class Renderer implements IRenderer { data.actionBar = null; data.container = null; data.entry = null; - data.keybinding.dispose(); data.keybinding = null; data.detail.dispose(); data.detail = null; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 45f5218b292..c5333366afe 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -999,7 +999,7 @@ export class ExtensionEditor extends BaseEditor { const renderKeybinding = (keybinding: ResolvedKeybinding): HTMLElement => { const element = $(''); - new KeybindingLabel(element, OS).set(keybinding, null); + new KeybindingLabel(element, OS).set(keybinding); return element; }; diff --git a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts index 8fc40dc505d..38ce9d93477 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingWidgets.ts @@ -257,10 +257,10 @@ export class DefineKeybindingWidget extends Widget { this._chordPart = chordPart; dom.clearNode(this._outputNode); dom.clearNode(this._showExistingKeybindingsNode); - new KeybindingLabel(this._outputNode, OS).set(this._firstPart, null); + new KeybindingLabel(this._outputNode, OS).set(this._firstPart); if (this._chordPart) { this._outputNode.appendChild(document.createTextNode(nls.localize('defineKeybinding.chordsTo', "chord to"))); - new KeybindingLabel(this._outputNode, OS).set(this._chordPart, null); + new KeybindingLabel(this._outputNode, OS).set(this._chordPart); } const label = this.getUserSettingsLabel(); if (label) { diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.css b/src/vs/workbench/parts/watermark/electron-browser/watermark.css index 9875886f652..8284b8442dd 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.css +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.css @@ -50,25 +50,6 @@ text-align: left; } -.monaco-workbench > .part.editor > .content.empty > .watermark dd > .shortcuts { - padding-left: 0.5em; - padding-right: 0.5em; -} - -.mac > .monaco-workbench > .part.editor > .content.empty > .watermark dd > .shortcuts { - letter-spacing: 0.15em; - font-family: "Lucida Grande", sans-serif; -} - -.monaco-workbench > .part.editor > .content.empty > .watermark dd > .unbound { - padding-left: 0.5em; - padding-right: 0.5em; -} - -.mac > .monaco-workbench > .part.editor > .content.empty > .watermark dd > .unbound { - font-family: "Lucida Grande", sans-serif; -} - .monaco-workbench > .part.editor > .content.empty > .watermark dt, .monaco-workbench > .part.editor > .content.empty > .watermark dd { display: table-cell; diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 35f3f10cd70..84b5334c1b5 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -6,7 +6,7 @@ import 'vs/css!./watermark'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; -import { isMacintosh } from 'vs/base/common/platform'; +import { isMacintosh, OS } from 'vs/base/common/platform'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -22,63 +22,63 @@ import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/comm import { Parts, IPartService, IDimension } from 'vs/workbench/services/part/common/partService'; import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions'; import { FindInFilesActionId } from 'vs/workbench/parts/search/common/constants'; -import { escape } from 'vs/base/common/strings'; import { QUICKOPEN_ACTION_ID } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; import * as dom from 'vs/base/browser/dom'; +import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; const $ = dom.$; interface WatermarkEntry { text: string; - ids: string[]; + id: string; mac?: boolean; } const showCommands: WatermarkEntry = { text: nls.localize('watermark.showCommands', "Show All Commands"), - ids: [ShowAllCommandsAction.ID] + id: ShowAllCommandsAction.ID }; const quickOpen: WatermarkEntry = { text: nls.localize('watermark.quickOpen', "Go to File"), - ids: [QUICKOPEN_ACTION_ID] + id: QUICKOPEN_ACTION_ID }; const openFileNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFile', "Open File"), - ids: [OpenFileAction.ID], + id: OpenFileAction.ID, mac: false }; const openFolderNonMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFolder', "Open Folder"), - ids: [OpenFolderAction.ID], + id: OpenFolderAction.ID, mac: false }; const openFileOrFolderMacOnly: WatermarkEntry = { text: nls.localize('watermark.openFileFolder', "Open File or Folder"), - ids: [OpenFileFolderAction.ID], + id: OpenFileFolderAction.ID, mac: true }; const openRecent: WatermarkEntry = { text: nls.localize('watermark.openRecent', "Open Recent"), - ids: [OpenRecentAction.ID] + id: OpenRecentAction.ID }; const newUntitledFile: WatermarkEntry = { text: nls.localize('watermark.newUntitledFile', "New Untitled File"), - ids: [GlobalNewUntitledFileAction.ID] + id: GlobalNewUntitledFileAction.ID }; const newUntitledFileMacOnly: WatermarkEntry = assign({ mac: true }, newUntitledFile); const toggleTerminal: WatermarkEntry = { text: nls.localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), - ids: [TERMINAL_COMMAND_ID.TOGGLE] + id: TERMINAL_COMMAND_ID.TOGGLE }; const findInFiles: WatermarkEntry = { text: nls.localize('watermark.findInFiles', "Find in Files"), - ids: [FindInFilesActionId] + id: FindInFilesActionId }; const startDebugging: WatermarkEntry = { text: nls.localize('watermark.startDebugging', "Start Debugging"), - ids: [StartAction.ID] + id: StartAction.ID }; const noFolderEntries = [ @@ -98,7 +98,6 @@ const folderEntries = [ toggleTerminal ]; -const UNBOUND = nls.localize('watermark.unboundCommand', "unbound"); const WORKBENCH_TIPS_ENABLED_KEY = 'workbench.tips.enabled'; export class WatermarkContribution implements IWorkbenchContribution { @@ -161,15 +160,9 @@ export class WatermarkContribution implements IWorkbenchContribution { const dt = dom.append(dl, $('dt')); dt.textContent = entry.text; const dd = dom.append(dl, $('dd')); - dd.innerHTML = entry.ids - .map(id => { - let k = this.keybindingService.lookupKeybinding(id); - if (k) { - return `${escape(k.getLabel())}`; - } - return `${escape(UNBOUND)}`; - }) - .join(' / '); + const keybinding = new KeybindingLabel(dd, OS, { renderUnboundKeybindings: true }); + keybinding.set(this.keybindingService.lookupKeybinding(entry.id)); + dd.innerHTML = keybinding.element.outerHTML; }); }; update(); From d1f3e563268cd84b5fcd6ad794141ffc60a4503a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 11:20:50 +0100 Subject: [PATCH 0333/1066] add reduce to chainable event --- src/vs/base/common/event.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 486793571f9..1a77679c4ff 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -93,8 +93,8 @@ export namespace Event { * Given an event and a `merge` function, returns another event which maps each element * and the cummulative result throught the `merge` function. Similar to `map`, but with memory. */ - export function reduce(event: Event, merge: (last: O | undefined, event: I) => O): Event { - let output: O | undefined = undefined; + export function reduce(event: Event, merge: (last: O | undefined, event: I) => O, initial?: O): Event { + let output: O | undefined = initial; return map(event, e => { output = merge(output, e); @@ -278,6 +278,7 @@ export namespace Event { map(fn: (i: T) => O): IChainableEvent; forEach(fn: (i: T) => void): IChainableEvent; filter(fn: (e: T) => boolean): IChainableEvent; + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; latch(): IChainableEvent; on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; @@ -301,6 +302,10 @@ export namespace Event { return new ChainableEvent(filter(this._event, fn)); } + reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent { + return new ChainableEvent(reduce(this._event, merge, initial)); + } + latch(): IChainableEvent { return new ChainableEvent(latch(this._event)); } From 44ac147e8ad014c66cf35a3a370f0bae30dc6b40 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 11:22:18 +0100 Subject: [PATCH 0334/1066] refactor list widget's type label controller --- src/vs/base/browser/ui/list/listWidget.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8dbfb0996f7..2f0c0312532 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -324,18 +324,18 @@ enum TypeLabelControllerState { Typing } -class TypeLabelController implements IDisposable { - - private static mightProducePrintableCharacter(event: IKeyboardEvent): boolean { - if (event.ctrlKey || event.metaKey || event.altKey) { - return false; - } - - return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) - || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9) - || (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE); +export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean { + if (event.ctrlKey || event.metaKey || event.altKey) { + return false; } + return (event.keyCode >= KeyCode.KEY_A && event.keyCode <= KeyCode.KEY_Z) + || (event.keyCode >= KeyCode.KEY_0 && event.keyCode <= KeyCode.KEY_9) + || (event.keyCode >= KeyCode.US_SEMICOLON && event.keyCode <= KeyCode.US_QUOTE); +} + +class TypeLabelController implements IDisposable { + private state: TypeLabelControllerState = TypeLabelControllerState.Idle; private disposables: IDisposable[] = []; @@ -347,7 +347,7 @@ class TypeLabelController implements IDisposable { const onChar = Event.chain(domEvent(view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(event => new StandardKeyboardEvent(event)) - .filter(keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => TypeLabelController.mightProducePrintableCharacter(e)) + .filter(keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e)) .map(event => event.browserEvent.key) .event; From aff4e25de2472aba2f582f9c1245e86ad49cc26b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 11:22:39 +0100 Subject: [PATCH 0335/1066] fix bad collapse action typing --- src/vs/workbench/browser/viewlet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 88ece005152..cfd30e38491 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -186,7 +186,7 @@ export class CollapseAction extends Action { // Collapse All action for the new tree export class CollapseAction2 extends Action { - constructor(tree: AsyncDataTree, enabled: boolean, clazz: string) { + constructor(tree: AsyncDataTree, enabled: boolean, clazz: string) { super('workbench.action.collapse', nls.localize('collapse', "Collapse All"), clazz, enabled, () => { tree.collapseAll(); return Promise.resolve(undefined); From add41d5b6560b5f005f9a7f42ef0e69457c60dd5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 11:25:14 +0100 Subject: [PATCH 0336/1066] index tree mode: expose filter functions --- src/vs/base/browser/ui/tree/indexTreeModel.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 105c30ea8dc..0b4fad00e98 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -19,10 +19,18 @@ interface IMutableTreeNode extends ITreeNode { filterData: TFilterData | undefined; } -function isFilterResult(obj: any): obj is ITreeFilterDataResult { +export function isFilterResult(obj: any): obj is ITreeFilterDataResult { return typeof obj === 'object' && 'visibility' in obj && 'data' in obj; } +export function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { + switch (visibility) { + case true: return TreeVisibility.Visible; + case false: return TreeVisibility.Hidden; + default: return visibility; + } +} + function treeNodeToElement(node: IMutableTreeNode): ITreeElement { const { element, collapsed } = node; const children = Iterator.map(Iterator.fromArray(node.children), treeNodeToElement); @@ -30,14 +38,6 @@ function treeNodeToElement(node: IMutableTreeNode): ITreeElement { return { element, children, collapsed }; } -function getVisibleState(visibility: boolean | TreeVisibility): TreeVisibility { - switch (visibility) { - case true: return TreeVisibility.Visible; - case false: return TreeVisibility.Hidden; - default: return visibility; - } -} - export interface IIndexTreeModelOptions { readonly collapseByDefault?: boolean; // defaults to false readonly filter?: ITreeFilter; From 1b61a70042a75e5f7273227b0974ffef51894adc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 11:36:23 +0100 Subject: [PATCH 0337/1066] =?UTF-8?q?strict=20m=C3=BCll?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts index 707c56b25ee..45b646c165f 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.ts @@ -34,7 +34,7 @@ export class KeybindingLabel { private domNode: HTMLElement; private keybinding: ResolvedKeybinding; - private matches: Matches; + private matches: Matches | undefined; private didEverRender: boolean; constructor(container: HTMLElement, private os: OperatingSystem, private options?: KeybindingLabelOptions) { @@ -108,7 +108,7 @@ export class KeybindingLabel { dom.append(parent, $('span.monaco-keybinding-key', undefined, localize('unbound', "Unbound"))); } - private static areSame(a: Matches, b: Matches): boolean { + private static areSame(a: Matches | undefined, b: Matches | undefined): boolean { if (a === b || (!a && !b)) { return true; } From 7587becd7320e4f58282f349a159bbc22b631520 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 17 Jan 2019 11:44:39 +0100 Subject: [PATCH 0338/1066] Support deregistering a composite --- src/tsconfig.strictNullChecks.json | 1 - .../electron-browser/mainThreadComments.ts | 15 +++- src/vs/workbench/browser/composite.ts | 15 +++- src/vs/workbench/browser/panel.ts | 7 ++ .../parts/activitybar/activitybarPart.ts | 16 ++-- .../workbench/browser/parts/compositePart.ts | 74 ++++++++++++------- .../browser/parts/panel/panelPart.ts | 50 +++++-------- .../browser/parts/sidebar/sidebarPart.ts | 24 +++--- src/vs/workbench/browser/viewlet.ts | 10 +++ .../electron-browser/comments.contribution.ts | 31 -------- .../browser/searchViewLocationUpdater.ts | 44 ----------- .../electron-browser/search.contribution.ts | 71 ++++++++++++------ .../services/panel/common/panelService.ts | 6 -- .../progress/test/progressService.test.ts | 5 +- .../services/viewlet/browser/viewlet.ts | 15 +--- .../workbench/test/workbenchTestServices.ts | 4 +- 16 files changed, 173 insertions(+), 215 deletions(-) delete mode 100644 src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 1bf494069ab..114996cfc82 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -672,7 +672,6 @@ "./vs/workbench/parts/search/browser/patternInputWidget.ts", "./vs/workbench/parts/search/browser/replaceContributions.ts", "./vs/workbench/parts/search/browser/replaceService.ts", - "./vs/workbench/parts/search/browser/searchViewLocationUpdater.ts", "./vs/workbench/parts/search/common/constants.ts", "./vs/workbench/parts/search/common/queryBuilder.ts", "./vs/workbench/parts/search/common/replace.ts", diff --git a/src/vs/workbench/api/electron-browser/mainThreadComments.ts b/src/vs/workbench/api/electron-browser/mainThreadComments.ts index ba0279f5390..1c6ebef3589 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadComments.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadComments.ts @@ -12,7 +12,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ExtHostCommentsShape, ExtHostContext, IExtHostContext, MainContext, MainThreadCommentsShape, CommentProviderFeatures } from '../node/extHost.protocol'; import { ICommentService } from 'vs/workbench/parts/comments/electron-browser/commentService'; -import { COMMENTS_PANEL_ID } from 'vs/workbench/parts/comments/electron-browser/commentsPanel'; +import { COMMENTS_PANEL_ID, CommentsPanel, COMMENTS_PANEL_TITLE } from 'vs/workbench/parts/comments/electron-browser/commentsPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { URI } from 'vs/base/common/uri'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -20,6 +20,8 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommentsConfiguration } from 'vs/workbench/parts/comments/electron-browser/comments.contribution'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; export class MainThreadDocumentCommentProvider implements modes.DocumentCommentProvider { private _proxy: ExtHostCommentsShape; @@ -133,11 +135,16 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._handlers.set(handle, providerId); const commentsPanelAlreadyConstructed = this._panelService.getPanels().some(panel => panel.id === COMMENTS_PANEL_ID); - this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, true); + Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + CommentsPanel, + COMMENTS_PANEL_ID, + COMMENTS_PANEL_TITLE, + 'commentsPanel', + 10 + )); const openPanel = this._configurationService.getValue('comments').openPanel; - if (openPanel === 'neverOpen') { this.registerOpenPanelListener(commentsPanelAlreadyConstructed); } @@ -180,7 +187,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments $unregisterWorkspaceCommentProvider(handle: number): void { this._workspaceProviders.delete(handle); if (this._workspaceProviders.size === 0) { - this._panelService.setPanelEnablement(COMMENTS_PANEL_ID, false); + Registry.as(PanelExtensions.Panels).deregisterPanel(COMMENTS_PANEL_ID); if (this._openPanelListener) { this._openPanelListener.dispose(); diff --git a/src/vs/workbench/browser/composite.ts b/src/vs/workbench/browser/composite.ts index 65bb9e04206..894aa1d77b9 100644 --- a/src/vs/workbench/browser/composite.ts +++ b/src/vs/workbench/browser/composite.ts @@ -215,8 +215,6 @@ export abstract class Composite extends Component implements IComposite { */ export abstract class CompositeDescriptor { - public enabled: boolean = true; - constructor( private readonly ctor: IConstructorSignature0, public readonly id: string, @@ -236,6 +234,9 @@ export abstract class CompositeRegistry extends Disposable private readonly _onDidRegister: Emitter> = this._register(new Emitter>()); get onDidRegister(): Event> { return this._onDidRegister.event; } + private readonly _onDidDeregister: Emitter> = this._register(new Emitter>()); + get onDidDeregister(): Event> { return this._onDidDeregister.event; } + private composites: CompositeDescriptor[] = []; protected registerComposite(descriptor: CompositeDescriptor): void { @@ -247,6 +248,16 @@ export abstract class CompositeRegistry extends Disposable this._onDidRegister.fire(descriptor); } + protected deregisterComposite(id: string): void { + const descriptor = this.compositeById(id); + if (descriptor === null) { + return; + } + + this.composites.splice(this.composites.indexOf(descriptor), 1); + this._onDidDeregister.fire(descriptor); + } + getComposite(id: string): CompositeDescriptor | null { return this.compositeById(id); } diff --git a/src/vs/workbench/browser/panel.ts b/src/vs/workbench/browser/panel.ts index 9aa0915c856..4cd74b0ebbe 100644 --- a/src/vs/workbench/browser/panel.ts +++ b/src/vs/workbench/browser/panel.ts @@ -34,6 +34,13 @@ export class PanelRegistry extends CompositeRegistry { super.registerComposite(descriptor); } + /** + * Deregisters a panel to the platform. + */ + deregisterPanel(id: string): void { + super.deregisterComposite(id); + } + /** * Returns an array of registered panels known to the platform. */ diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 35b44eccc6c..372cacb08fd 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -93,25 +93,19 @@ export class ActivitybarPart extends Part { })); this.registerListeners(); - this.onDidRegisterViewlets(viewletService.getAllViewlets()); + this.onDidRegisterViewlets(viewletService.getViewlets()); } private registerListeners(): void { this._register(this.viewletService.onDidViewletRegister(viewlet => this.onDidRegisterViewlets([viewlet]))); + this._register(this.viewletService.onDidViewletDeregister(({ id }) => this.removeComposite(id, true))); // Activate viewlet action on opening of a viewlet this._register(this.viewletService.onDidViewletOpen(viewlet => this.onDidViewletOpen(viewlet))); // Deactivate viewlet action on close this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); - this._register(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => { - if (enabled) { - this.compositeBar.addComposite(this.viewletService.getViewlet(id)); - } else { - this.removeComposite(id, true); - } - })); let disposables: IDisposable[] = []; this._register(this.extensionService.onDidRegisterExtensions(() => { @@ -125,7 +119,7 @@ export class ActivitybarPart extends Part { private onDidRegisterExtensions(): void { this.removeNotExistingComposites(); - for (const viewlet of this.viewletService.getAllViewlets()) { + for (const viewlet of this.viewletService.getViewlets()) { this.enableCompositeActions(viewlet); const viewContainer = this.getViewContainer(viewlet.id); if (viewContainer) { @@ -299,7 +293,7 @@ export class ActivitybarPart extends Part { } private removeNotExistingComposites(): void { - const viewlets = this.viewletService.getAllViewlets(); + const viewlets = this.viewletService.getViewlets(); for (const { id } of this.cachedViewlets) { if (viewlets.every(viewlet => viewlet.id !== id)) { this.removeComposite(id, false); @@ -395,7 +389,7 @@ export class ActivitybarPart extends Part { private saveCachedViewlets(): void { const state: ICachedViewlet[] = []; const compositeItems = this.compositeBar.getCompositeBarItems(); - const allViewlets = this.viewletService.getAllViewlets(); + const allViewlets = this.viewletService.getViewlets(); for (const compositeItem of compositeItems) { const viewContainer = this.getViewContainer(compositeItem.id); const viewlet = allViewlets.filter(({ id }) => id === compositeItem.id)[0]; diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index 7c06dab915f..1865d8b1d70 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/compositepart'; import * as nls from 'vs/nls'; import { defaultGenerator } from 'vs/base/common/idGenerator'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { Emitter } from 'vs/base/common/event'; import * as errors from 'vs/base/common/errors'; @@ -46,6 +46,12 @@ export interface ICompositeTitleLabel { updateStyles(): void; } +interface CompositeItem { + composite: Composite; + disposable: IDisposable; + progressService: IProgressService; +} + export abstract class CompositePart extends Part { protected _onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>()); @@ -53,13 +59,11 @@ export abstract class CompositePart extends Part { protected toolBar: ToolBar; - private instantiatedCompositeListeners: IDisposable[]; private mapCompositeToCompositeContainer: { [compositeId: string]: HTMLElement; }; private mapActionsBindingToComposite: { [compositeId: string]: () => void; }; - private mapProgressServiceToComposite: { [compositeId: string]: IProgressService; }; private activeComposite: Composite | null; private lastActiveCompositeId: string; - private instantiatedComposites: Composite[]; + private instantiatedCompositeItems: Map; private titleLabel: ICompositeTitleLabel; private progressBar: ProgressBar; private contentAreaSize: Dimension; @@ -86,12 +90,10 @@ export abstract class CompositePart extends Part { ) { super(id, options, themeService, storageService); - this.instantiatedCompositeListeners = []; this.mapCompositeToCompositeContainer = {}; this.mapActionsBindingToComposite = {}; - this.mapProgressServiceToComposite = {}; this.activeComposite = null; - this.instantiatedComposites = []; + this.instantiatedCompositeItems = new Map(); this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); } @@ -159,10 +161,9 @@ export abstract class CompositePart extends Part { protected createComposite(id: string, isActive?: boolean): Composite { // Check if composite is already created - for (const composite of this.instantiatedComposites) { - if (composite.getId() === id) { - return composite; - } + const compositeItem = this.instantiatedCompositeItems.get(id); + if (compositeItem) { + return compositeItem.composite; } // Instantiate composite from registry otherwise @@ -172,13 +173,13 @@ export abstract class CompositePart extends Part { const compositeInstantiationService = this.instantiationService.createChild(new ServiceCollection([IProgressService, progressService])); const composite = compositeDescriptor.instantiate(compositeInstantiationService); - this.mapProgressServiceToComposite[composite.getId()] = progressService; + const disposables: IDisposable[] = []; // Remember as Instantiated - this.instantiatedComposites.push(composite); + this.instantiatedCompositeItems.set(id, { composite, disposable: toDisposable(() => dispose(disposables)), progressService }); // Register to title area update events from the composite - this.instantiatedCompositeListeners.push(composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()))); + composite.onTitleAreaUpdate(() => this.onTitleAreaUpdate(composite.getId()), this, disposables); return composite; } @@ -219,9 +220,9 @@ export abstract class CompositePart extends Part { } // Report progress for slow loading composites (but only if we did not create the composites before already) - const progressService = this.mapProgressServiceToComposite[composite.getId()]; - if (progressService && !compositeContainer) { - this.mapProgressServiceToComposite[composite.getId()].showWhile(Promise.resolve(), this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */); + const compositeItem = this.instantiatedCompositeItems.get(composite.getId()); + if (compositeItem && !compositeContainer) { + compositeItem.progressService.showWhile(Promise.resolve(), this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */); } // Fill Content and Actions @@ -446,8 +447,9 @@ export abstract class CompositePart extends Part { return contentContainer; } - getProgressIndicator(id: string): IProgressService { - return this.mapProgressServiceToComposite[id]; + getProgressIndicator(id: string): IProgressService | null { + const compositeItem = this.instantiatedCompositeItems.get(id); + return compositeItem ? compositeItem.progressService : null; } protected getActions(): IAction[] { @@ -476,17 +478,33 @@ export abstract class CompositePart extends Part { return sizes; } - dispose(): void { - this.mapCompositeToCompositeContainer = null!; // StrictNullOverride: nulling out ok in dispose - this.mapProgressServiceToComposite = null!; // StrictNullOverride: nulling out ok in dispose - this.mapActionsBindingToComposite = null!; // StrictNullOverride: nulling out ok in dispose - - for (const composite of this.instantiatedComposites) { - composite.dispose(); + protected removeComposite(compositeId: string): boolean { + if (this.activeComposite && this.activeComposite.getId() === compositeId) { + // do not remove active compoiste + return false; } - this.instantiatedComposites = []; - this.instantiatedCompositeListeners = dispose(this.instantiatedCompositeListeners); + delete this.mapCompositeToCompositeContainer[compositeId]; + delete this.mapActionsBindingToComposite[compositeId]; + const compositeItem = this.instantiatedCompositeItems.get(compositeId); + if (compositeItem) { + compositeItem.composite.dispose(); + dispose(compositeItem.disposable); + this.instantiatedCompositeItems.delete(compositeId); + } + return true; + } + + dispose(): void { + this.mapCompositeToCompositeContainer = null!; // StrictNullOverride: nulling out ok in dispose + this.mapActionsBindingToComposite = null!; // StrictNullOverride: nulling out ok in dispose + + this.instantiatedCompositeItems.forEach(compositeItem => { + compositeItem.composite.dispose(); + dispose(compositeItem.disposable); + }); + + this.instantiatedCompositeItems.clear(); super.dispose(); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 9c057aa19d1..26a6b426ed8 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -146,6 +146,10 @@ export class PanelPart extends CompositePart implements IPanelService { this._register(this.onDidPanelClose(this._onDidPanelClose, this)); this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor))); + this._register(this.registry.onDidDeregister(panelDescriptor => { + this.compositeBar.hideComposite(panelDescriptor.id); + this.removeComposite(panelDescriptor.id); + })); // Activate panel action on opening of a panel this._register(this.onDidPanelOpen(({ panel }) => { @@ -220,15 +224,10 @@ export class PanelPart extends CompositePart implements IPanelService { } getPanels(): PanelDescriptor[] { - return this.getAllPanels() - .filter(p => p.enabled) + return Registry.as(PanelExtensions.Panels).getPanels() .sort((v1, v2) => v1.order - v2.order); } - private getAllPanels() { - return Registry.as(PanelExtensions.Panels).getPanels(); - } - getPinnedPanels(): PanelDescriptor[] { const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(c => c.id); return this.getPanels() @@ -236,18 +235,6 @@ export class PanelPart extends CompositePart implements IPanelService { .sort((p1, p2) => pinnedCompositeIds.indexOf(p1.id) - pinnedCompositeIds.indexOf(p2.id)); } - setPanelEnablement(id: string, enabled: boolean): void { - const descriptor = Registry.as(PanelExtensions.Panels).getPanels().filter(p => p.id === id).pop(); - if (descriptor && descriptor.enabled !== enabled) { - descriptor.enabled = enabled; - if (enabled) { - this.compositeBar.addComposite(descriptor); - } else { - this.removeComposite(id); - } - } - } - protected getActions(): IAction[] { return [ this.instantiationService.createInstance(ToggleMaximizedPanelAction, ToggleMaximizedPanelAction.ID, ToggleMaximizedPanelAction.LABEL), @@ -324,14 +311,17 @@ export class PanelPart extends CompositePart implements IPanelService { return compositeActions; } - private removeComposite(compositeId: string): void { - this.compositeBar.hideComposite(compositeId); - const compositeActions = this.compositeActions[compositeId]; - if (compositeActions) { - compositeActions.activityAction.dispose(); - compositeActions.pinnedAction.dispose(); - delete this.compositeActions[compositeId]; + protected removeComposite(compositeId: string): boolean { + if (super.removeComposite(compositeId)) { + const compositeActions = this.compositeActions[compositeId]; + if (compositeActions) { + compositeActions.activityAction.dispose(); + compositeActions.pinnedAction.dispose(); + delete this.compositeActions[compositeId]; + } + return true; } + return false; } private getToolbarWidth(): number { @@ -378,21 +368,19 @@ export class PanelPart extends CompositePart implements IPanelService { private saveCachedPanels(): void { const state: ICachedPanel[] = []; const compositeItems = this.compositeBar.getCompositeBarItems(); - const allPanels = this.getAllPanels(); for (const compositeItem of compositeItems) { - const panel = allPanels.filter(({ id }) => id === compositeItem.id)[0]; - if (panel) { - state.push({ id: panel.id, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible }); - } + state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible }); } this.cachedPanelsValue = JSON.stringify(state); } private getCachedPanels(): ICachedPanel[] { const storedStates = >JSON.parse(this.cachedPanelsValue); + const registeredPanels = this.getPanels(); const cachedPanels = storedStates.map(c => { const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true } : c; - serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible; + const registered = registeredPanels.some(p => p.id === serialized.id); + serialized.visible = registered ? isUndefinedOrNull(serialized.visible) ? true : serialized.visible : false; return serialized; }); return cachedPanels; diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 0d03a8851f7..f357916019f 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -43,7 +43,7 @@ export class SidebarPart extends CompositePart implements IViewletServi private sideBarFocusContextKey: IContextKey; private activeViewletContextKey: IContextKey; private blockOpeningViewlet: boolean; - private _onDidViewletEnable = new Emitter<{ id: string, enabled: boolean }>(); + private _onDidViewletDeregister = this._register(new Emitter()); constructor( id: string, @@ -90,10 +90,17 @@ export class SidebarPart extends CompositePart implements IViewletServi this.activeViewletContextKey.reset(); } })); + this._register(this.registry.onDidDeregister(async (viewletDescriptor: ViewletDescriptor) => { + if (this.getActiveViewlet().getId() === viewletDescriptor.id) { + await this.openViewlet(this.getDefaultViewletId()); + } + this.removeComposite(viewletDescriptor.id); + this._onDidViewletDeregister.fire(viewletDescriptor); + })); } get onDidViewletRegister(): Event { return >this.viewletRegistry.onDidRegister; } - get onDidViewletEnablementChange(): Event<{ id: string, enabled: boolean }> { return this._onDidViewletEnable.event; } + get onDidViewletDeregister(): Event { return this._onDidViewletDeregister.event; } get onDidViewletOpen(): Event { return Event.map(this._onDidCompositeOpen.event, compositeEvent => compositeEvent.composite); @@ -167,14 +174,6 @@ export class SidebarPart extends CompositePart implements IViewletServi this.hideActiveComposite(); } - setViewletEnablement(id: string, enabled: boolean): void { - const descriptor = this.getAllViewlets().filter(desc => desc.id === id).pop(); - if (descriptor && descriptor.enabled !== enabled) { - descriptor.enabled = enabled; - this._onDidViewletEnable.fire({ id, enabled }); - } - } - openViewlet(id: string, focus?: boolean): Promise { if (this.getViewlet(id)) { return Promise.resolve(this.doOpenViewlet(id, focus)); @@ -189,11 +188,6 @@ export class SidebarPart extends CompositePart implements IViewletServi } getViewlets(): ViewletDescriptor[] { - return this.getAllViewlets() - .filter(v => v.enabled); - } - - getAllViewlets(): ViewletDescriptor[] { return this.viewletRegistry.getViewlets() .sort((v1, v2) => v1.order! - v2.order!); } diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index 88ece005152..b2357df281a 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -86,6 +86,16 @@ export class ViewletRegistry extends CompositeRegistry { super.registerComposite(descriptor); } + /** + * Deregisters a viewlet to the platform. + */ + deregisterViewlet(id: string): void { + if (id === this.defaultViewletId) { + throw new Error('Cannot deregister default viewlet'); + } + super.deregisterComposite(id); + } + /** * Returns the viewlet descriptor for the given id or null if none. */ diff --git a/src/vs/workbench/parts/comments/electron-browser/comments.contribution.ts b/src/vs/workbench/parts/comments/electron-browser/comments.contribution.ts index 0fa44802858..37421047f9c 100644 --- a/src/vs/workbench/parts/comments/electron-browser/comments.contribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/comments.contribution.ts @@ -4,13 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; -import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { COMMENTS_PANEL_ID, COMMENTS_PANEL_TITLE, CommentsPanel } from './commentsPanel'; import 'vs/workbench/parts/comments/electron-browser/commentsEditorContribution'; import { ICommentService, CommentService } from 'vs/workbench/parts/comments/electron-browser/commentService'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -19,29 +14,6 @@ export interface ICommentsConfiguration { openPanel: 'neverOpen' | 'openOnSessionStart' | 'openOnSessionStartWithComments'; } -export class CommentPanelVisibilityUpdater implements IWorkbenchContribution { - - constructor( - @IPanelService panelService: IPanelService - ) { - // commentsProviderRegistry.onChange - const updateCommentPanelVisibility = () => { - panelService.setPanelEnablement(COMMENTS_PANEL_ID, false); - }; - - updateCommentPanelVisibility(); - } -} - -Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( - CommentsPanel, - COMMENTS_PANEL_ID, - COMMENTS_PANEL_TITLE, - 'commentsPanel', - 10 -)); - - Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ id: 'comments', order: 20, @@ -56,7 +28,4 @@ Registry.as(ConfigurationExtensions.Configuration).regis } }); -// Register view location updater -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(CommentPanelVisibilityUpdater, LifecyclePhase.Starting); - registerSingleton(ICommentService, CommentService); diff --git a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts b/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts deleted file mode 100644 index d5b6bf21e19..00000000000 --- a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.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 { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ISearchConfiguration, VIEW_ID } from 'vs/platform/search/common/search'; - -export class SearchViewLocationUpdater implements IWorkbenchContribution { - - constructor( - @IViewletService viewletService: IViewletService, - @IPanelService panelService: IPanelService, - @IConfigurationService configurationService: IConfigurationService - ) { - const updateSearchViewLocation = (open: boolean) => { - const config = configurationService.getValue(); - if (config.search.location === 'panel') { - viewletService.setViewletEnablement(VIEW_ID, false); - panelService.setPanelEnablement(VIEW_ID, true); - if (open) { - panelService.openPanel(VIEW_ID); - } - } else { - panelService.setPanelEnablement(VIEW_ID, false); - viewletService.setViewletEnablement(VIEW_ID, true); - if (open) { - viewletService.openViewlet(VIEW_ID); - } - } - }; - - configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('search.location')) { - updateSearchViewLocation(true); - } - }); - - updateSearchViewLocation(false); - } -} 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 f5b14fe336d..6f625f801ef 100644 --- a/src/vs/workbench/parts/search/electron-browser/search.contribution.ts +++ b/src/vs/workbench/parts/search/electron-browser/search.contribution.ts @@ -32,13 +32,11 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IListService, WorkbenchListFocusContextKey, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ISearchConfigurationProperties, VIEW_ID } from 'vs/platform/search/common/search'; -import { Extensions as PanelExtensions, PanelDescriptor, PanelRegistry } from 'vs/workbench/browser/panel'; +import { ISearchConfigurationProperties, VIEW_ID, ISearchConfiguration } from 'vs/platform/search/common/search'; import { defaultQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { Extensions as ViewletExtensions, ViewletDescriptor, ViewletRegistry } from 'vs/workbench/browser/viewlet'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/files'; import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition } from 'vs/workbench/parts/files/common/files'; @@ -46,8 +44,6 @@ import { OpenAnythingHandler } from 'vs/workbench/parts/search/browser/openAnyth import { OpenSymbolHandler } from 'vs/workbench/parts/search/browser/openSymbolHandler'; import { registerContributions as replaceContributions } from 'vs/workbench/parts/search/browser/replaceContributions'; import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FindInFilesAction, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand } from 'vs/workbench/parts/search/browser/searchActions'; -import { SearchView } from 'vs/workbench/parts/search/browser/searchView'; -import { SearchViewLocationUpdater } from 'vs/workbench/parts/search/browser/searchViewLocationUpdater'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/parts/search/browser/searchWidget'; import * as Constants from 'vs/workbench/parts/search/common/constants'; import { getWorkspaceSymbols } from 'vs/workbench/parts/search/common/search'; @@ -55,6 +51,9 @@ import { FileMatchOrMatch, ISearchWorkbenchService, RenderableMatch, SearchWorkb import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel'; +import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; +import { SearchView } from 'vs/workbench/parts/search/browser/searchView'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); replaceContributions(); @@ -450,25 +449,51 @@ class ShowAllSymbolsAction extends Action { } } -// Register View in Viewlet and Panel area -Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( - SearchView, - VIEW_ID, - nls.localize('name', "Search"), - 'search', - 1 -)); +class RegisterSearchViewContribution implements IWorkbenchContribution { -Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( - SearchView, - VIEW_ID, - nls.localize('name', "Search"), - 'search', - 10 -)); + constructor( + @IViewletService viewletService: IViewletService, + @IPanelService panelService: IPanelService, + @IConfigurationService configurationService: IConfigurationService + ) { + const updateSearchViewLocation = (open: boolean) => { + const config = configurationService.getValue(); + if (config.search.location === 'panel') { + Registry.as(ViewletExtensions.Viewlets).deregisterViewlet(VIEW_ID); + Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescriptor( + SearchView, + VIEW_ID, + nls.localize('name', "Search"), + 'search', + 10 + )); + if (open) { + panelService.openPanel(VIEW_ID); + } + } else { + Registry.as(PanelExtensions.Panels).deregisterPanel(VIEW_ID); + Registry.as(ViewletExtensions.Viewlets).registerViewlet(new ViewletDescriptor( + SearchView, + VIEW_ID, + nls.localize('name', "Search"), + 'search', + 1 + )); + if (open) { + viewletService.openViewlet(VIEW_ID); + } + } + }; + configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('search.location')) { + updateSearchViewLocation(true); + } + }); -// Register view location updater -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SearchViewLocationUpdater, LifecyclePhase.Starting); + updateSearchViewLocation(false); + } +} +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(RegisterSearchViewContribution, LifecyclePhase.Starting); // Actions const registry = Registry.as(ActionExtensions.WorkbenchActions); diff --git a/src/vs/workbench/services/panel/common/panelService.ts b/src/vs/workbench/services/panel/common/panelService.ts index 80a8a7e07f4..11732a0207a 100644 --- a/src/vs/workbench/services/panel/common/panelService.ts +++ b/src/vs/workbench/services/panel/common/panelService.ts @@ -41,10 +41,4 @@ export interface IPanelService { * Returns pinned panels following the visual order */ getPinnedPanels(): IPanelIdentifier[]; - - /** - * Enables or disables a panel. Disabled panels are completly hidden from UI. - * By default all panels are enabled. - */ - setPanelEnablement(id: string, enabled: boolean): void; } diff --git a/src/vs/workbench/services/progress/test/progressService.test.ts b/src/vs/workbench/services/progress/test/progressService.test.ts index 2f1e0fbf611..5a1073e1c0e 100644 --- a/src/vs/workbench/services/progress/test/progressService.test.ts +++ b/src/vs/workbench/services/progress/test/progressService.test.ts @@ -20,14 +20,14 @@ class TestViewletService implements IViewletService { public _serviceBrand: any; onDidViewletRegisterEmitter = new Emitter(); + onDidViewletDeregisterEmitter = new Emitter(); onDidViewletOpenEmitter = new Emitter(); onDidViewletCloseEmitter = new Emitter(); - onDidViewletEnableEmitter = new Emitter<{ id: string, enabled: boolean }>(); onDidViewletRegister = this.onDidViewletRegisterEmitter.event; + onDidViewletDeregister = this.onDidViewletDeregisterEmitter.event; onDidViewletOpen = this.onDidViewletOpenEmitter.event; onDidViewletClose = this.onDidViewletCloseEmitter.event; - onDidViewletEnablementChange = this.onDidViewletEnableEmitter.event; public openViewlet(id: string, focus?: boolean): Promise { return Promise.resolve(null!); @@ -44,7 +44,6 @@ class TestViewletService implements IViewletService { public getActiveViewlet(): IViewlet { return activeViewlet; } - public setViewletEnablement(id: string, enabled: boolean): void { } public dispose() { } diff --git a/src/vs/workbench/services/viewlet/browser/viewlet.ts b/src/vs/workbench/services/viewlet/browser/viewlet.ts index b03de9f75f3..c3345bae93c 100644 --- a/src/vs/workbench/services/viewlet/browser/viewlet.ts +++ b/src/vs/workbench/services/viewlet/browser/viewlet.ts @@ -15,9 +15,9 @@ export interface IViewletService { _serviceBrand: ServiceIdentifier; onDidViewletRegister: Event; + onDidViewletDeregister: Event; onDidViewletOpen: Event; onDidViewletClose: Event; - onDidViewletEnablementChange: Event<{ id: string, enabled: boolean }>; /** * Opens a viewlet with the given identifier and pass keyboard focus to it if specified. @@ -39,24 +39,13 @@ export interface IViewletService { */ getViewlet(id: string): ViewletDescriptor; - /** - * Returns all enabled viewlets following the default order (Explorer - Search - SCM - Debug - Extensions) - */ - getAllViewlets(): ViewletDescriptor[]; - /** * Returns all enabled viewlets */ getViewlets(): ViewletDescriptor[]; - /** - * Enables or disables a viewlet. Disabled viewlets are completly hidden from UI. - * By default all viewlets are enabled. - */ - setViewletEnablement(id: string, enabled: boolean): void; - /** * */ - getProgressIndicator(id: string): IProgressService; + getProgressIndicator(id: string): IProgressService | null; } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 5f79471a395..fc7000c55a3 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -1451,9 +1451,9 @@ export class TestViewletService implements IViewletService { _serviceBrand: ServiceIdentifier; readonly onDidViewletRegister: Event = new Emitter().event; + readonly onDidViewletDeregister: Event = new Emitter().event; onDidViewletOpen: Event = new Emitter().event; onDidViewletClose: Event = new Emitter().event; - onDidViewletEnablementChange: Event<{ id: string, enabled: boolean }> = new Emitter<{ id: string, enabled: boolean }>().event; openViewlet(_id: string, _focus?: boolean): Promise { return null; } @@ -1467,8 +1467,6 @@ export class TestViewletService implements IViewletService { getViewlets(): ViewletDescriptor[] { return null; } - setViewletEnablement(_id: string, _enabled: boolean): void { } - getProgressIndicator(_id: string): IProgressService { return null; } } From e71cf22a95bc81b31e4fdd71c7a749c7ef519e6f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 15:23:24 +0100 Subject: [PATCH 0339/1066] go menu - use commands for navigating across files for problems --- src/vs/editor/contrib/gotoError/gotoError.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 64f7b1fe292..83281459b5e 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -474,7 +474,7 @@ registerEditorCommand(new MarkerCommand({ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '6_problem_nav', command: { - id: 'editor.action.marker.next', + id: 'editor.action.marker.nextInFiles', title: nls.localize({ key: 'miGotoNextProblem', comment: ['&& denotes a mnemonic'] }, "Next &&Problem") }, order: 1 @@ -483,7 +483,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { group: '6_problem_nav', command: { - id: 'editor.action.marker.prev', + id: 'editor.action.marker.prevInFiles', title: nls.localize({ key: 'miGotoPreviousProblem', comment: ['&& denotes a mnemonic'] }, "Previous &&Problem") }, order: 2 From 7602a83cfac9d6f73cf3b15e381b1227d9974f8c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 15:57:07 +0100 Subject: [PATCH 0340/1066] fix some issues from LGTM --- src/vs/editor/contrib/referenceSearch/referencesWidget.ts | 2 +- src/vs/workbench/services/textfile/common/textFileService.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 6a3e9a797ac..a5e63476dbb 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -398,7 +398,7 @@ export class ReferenceWidget extends PeekViewWidget { goto = true; } else if (e.browserEvent instanceof MouseEvent) { - aside = e.browserEvent.metaKey || e.browserEvent.metaKey || e.browserEvent.altKey; + aside = e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey; goto = e.browserEvent.detail === 2; } if (aside) { diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 30c7d3e9527..8babc6bcb54 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -386,10 +386,6 @@ export abstract class TextFileService extends Disposable implements ITextFileSer if (options && options.force && this.fileService.canHandleResource(resource) && !this.isDirty(resource)) { const model = this._models.get(resource); if (model) { - if (!options) { - options = Object.create(null); - } - options.reason = SaveReason.EXPLICIT; return model.save(options).then(() => !model.isDirty()); From e982c58b8c45177b87dc2247aba9fbded6402653 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 16:01:37 +0100 Subject: [PATCH 0341/1066] simplify evaluation --- .../parts/debug/electron-browser/debugEditorContribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts index 7290b663198..f6350174f3a 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugEditorContribution.ts @@ -294,7 +294,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (stackFrame && model && model.uri.toString() === stackFrame.source.uri.toString()) { this.editor.updateOptions({ hover: { - enabled: !stackFrame || !model || model.uri.toString() !== stackFrame.source.uri.toString() + enabled: false } }); } else { From d47516e1bf7ea8aa60ee3dcbc049f543363ac207 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 16:16:21 +0100 Subject: [PATCH 0342/1066] wip: better tree keyboard navigation --- src/vs/base/browser/ui/tree/abstractTree.ts | 139 ++++++++++++++++-- src/vs/base/browser/ui/tree/media/tree.css | 14 ++ .../electron-browser/views/explorerView.ts | 3 +- .../electron-browser/views/explorerViewer.ts | 18 ++- 4 files changed, 153 insertions(+), 21 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e8e7cdeeea4..20af5997ee3 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,17 +5,20 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, timeout } from 'vs/base/browser/dom'; import { Event, Relay } from 'vs/base/common/event'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { range } from 'vs/base/common/arrays'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { domEvent } from 'vs/base/browser/event'; +import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; +import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -133,11 +136,7 @@ function asListOptions(modelProvider: () => ITreeModel implements IListRenderer implements ITreeFilter { + + private _pattern: string; + private _lowercasePattern: string; + + set pattern(pattern: string) { + this._pattern = pattern; + this._lowercasePattern = pattern.toLowerCase(); + } + + constructor( + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, + private _filter?: ITreeFilter + ) { } + + filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { + if (this._filter) { + const result = this._filter.filter(element, parentVisibility); + let visibility: TreeVisibility; + + if (typeof result === 'boolean') { + visibility = result ? TreeVisibility.Visible : TreeVisibility.Hidden; + } else if (isFilterResult(result)) { + visibility = getVisibleState(result.visibility); + } else { + visibility = result; + } + + if (visibility === TreeVisibility.Hidden) { + return false; + } + } + + if (!this._pattern) { + return { data: FuzzyScore.Default, visibility: true }; + } + + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element).toString(); + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, label, label.toLowerCase(), 0, true); + + if (!score) { + // DEMO: just highlights + return { data: FuzzyScore.Default, visibility: true }; + + // DEMO: filter + // return TreeVisibility.Recurse; + + // DEMO: smarter filter + // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + } + + return { data: score, visibility: true }; + } +} + +class TypeFilterController implements IDisposable { + + private domNode: HTMLElement; + private disposables: IDisposable[] = []; + + constructor( + private tree: AbstractTree, + view: List>, + private filter: TypeFilter, + keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + ) { + const container = view.getHTMLElement(); + this.domNode = append(container, $('.monaco-tl-type-filter')); + + const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); + const onInput = Event.chain(domEvent(container, 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement)) + .map(e => new StandardKeyboardEvent(e)) + .filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e)) + .forEach(e => { e.stopPropagation(); e.preventDefault(); }) + .reduce((previous: string, e) => { + if (e.keyCode === KeyCode.Escape) { + return ''; + } + + if (e.keyCode === KeyCode.Backspace) { + return previous.length === 0 ? '' : previous.substr(0, previous.length - 1); + } + + return previous + e.browserEvent.key; + }, '') + .event; + + onInput(this.onInput, this, this.disposables); + } + + private onInput(pattern: string): void { + this.domNode.textContent = pattern; + this.filter.pattern = pattern; + this.tree.refilter(); + } + + dispose() { + this.disposables = dispose(this.disposables); + } +} + function isInputElement(e: HTMLElement): boolean { return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; } @@ -291,9 +392,7 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IAbstractTreeOptionsUpdate, IListOptions { readonly collapseByDefault?: boolean; // defaults to false @@ -302,6 +401,10 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly autoExpandSingleChildren?: boolean; } +export interface IAbstractTreeWithTypeFilterOptions extends IAbstractTreeOptions { + readonly keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider; +} + export abstract class AbstractTree implements IDisposable { private view: List>; @@ -341,6 +444,13 @@ 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; + + if (options.keyboardNavigationLabelProvider) { + filter = new TypeFilter(options.keyboardNavigationLabelProvider, options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here + options = { ...options, filter }; + } + this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, options)); this.model = this.createModel(this.view, options); @@ -357,6 +467,11 @@ export abstract class AbstractTree implements IDisposable onKeyDown.filter(e => e.keyCode === KeyCode.RightArrow).on(this.onRightArrow, this, this.disposables); onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } + + if (options.keyboardNavigationLabelProvider) { + const typeFilterController = new TypeFilterController(this, this.view, filter as TypeFilter, options.keyboardNavigationLabelProvider); + this.disposables.push(typeFilterController); + } } updateOptions(options: IAbstractTreeOptionsUpdate = {}): void { diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index aa46437ecd7..48d2eced3e8 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -65,4 +65,18 @@ .hc-black .monaco-tl-twistie.loading { background-image: url("loading-hc.svg"); +} + +.monaco-tl-type-filter { + position: absolute; + right: 5px; + top: 4px; + border-radius: 2px; + background: #0947718a; /* TODO@joao: theme */ + border: 1px solid #8080808a; + padding: 0px 3px; +} + +.monaco-tl-type-filter:empty { + display: none; } \ No newline at end of file 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 30e35834397..abff9930d84 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -45,12 +45,13 @@ import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/l import { createFileIconThemableTreeContainerScope } from 'vs/workbench/browser/parts/views/views'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { FuzzyScore } from 'vs/base/common/filters'; export class ExplorerView extends ViewletPanel { static readonly ID: string = 'workbench.explorer.fileView'; static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState'; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private filter: FilesFilter; private resourceContext: ResourceContextKey; 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 b232d3ca572..a40f55ef664 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -46,6 +46,7 @@ import { ITask, sequence } from 'vs/base/common/async'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { findValidPasteFileTarget } from 'vs/workbench/parts/files/electron-browser/fileActions'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; export class ExplorerDelegate implements IListVirtualDelegate { @@ -98,7 +99,7 @@ export interface IFileTemplateData { container: HTMLElement; } -export class FilesRenderer implements ITreeRenderer, IDisposable { +export class FilesRenderer implements ITreeRenderer, IDisposable { static readonly ID = 'file'; private config: IFilesConfiguration; @@ -125,14 +126,14 @@ export class FilesRenderer implements ITreeRenderer, index: number, templateData: IFileTemplateData): void { + renderElement(node: ITreeNode, index: number, templateData: IFileTemplateData): void { templateData.elementDisposable.dispose(); - const stat = element.element; + const stat = node.element; const editableData = this.explorerService.getEditableData(stat); // File Label @@ -143,7 +144,8 @@ export class FilesRenderer implements ITreeRenderer { @@ -241,7 +243,7 @@ export class FilesRenderer implements ITreeRenderer ignoreBlur = true); } - disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { + disposeElement?(element: ITreeNode, index: number, templateData: IFileTemplateData): void { // noop } @@ -266,7 +268,7 @@ interface CachedParsedExpression { parsed: glob.ParsedExpression; } -export class FilesFilter implements ITreeFilter { +export class FilesFilter implements ITreeFilter { private hiddenExpressionPerRoot: Map; private workspaceFolderChangeListener: IDisposable; @@ -298,7 +300,7 @@ export class FilesFilter implements ITreeFilter { return needsRefresh; } - filter(stat: ExplorerItem, parentVisibility: TreeVisibility): TreeFilterResult { + filter(stat: ExplorerItem, parentVisibility: TreeVisibility): TreeFilterResult { if (parentVisibility === TreeVisibility.Hidden) { return false; } From ca8ed3ce25ed3d7dbfb5d0e80d3fa1eea3f1cc5f Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 16:55:17 +0100 Subject: [PATCH 0343/1066] explorer: root need sto be refreshed when an element is selected fixes #66630 --- src/vs/workbench/parts/files/electron-browser/explorerService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 0428d4d3539..766e6dc5508 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -125,6 +125,7 @@ export class ExplorerService implements IExplorerService { const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); + this._onDidChangeItem.fire(root); // Select and Reveal this._onDidSelectItem.fire({ item: root.find(resource) || undefined, reveal }); From e027751b0873004b74f09949623b794e13053647 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 17:01:07 +0100 Subject: [PATCH 0344/1066] fixes #66566 --- .../parts/files/electron-browser/views/explorerView.ts | 4 ++-- 1 file changed, 2 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 30e35834397..ac1272523f5 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -294,9 +294,9 @@ export class ExplorerView extends ViewletPanel { const selection = e.elements; // Do not react if the user is expanding selection via keyboard. // Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589. - const wasSelectedAndKeyboard = (e.browserEvent instanceof KeyboardEvent) && this.previousSelection.indexOf(selection[0]) >= 0; + const wasMultiSelectedAndKeyboard = (e.browserEvent instanceof KeyboardEvent) && this.previousSelection.length > 1 && this.previousSelection.indexOf(selection[0]) >= 0; this.previousSelection = selection; - if (selection.length === 1 && !wasSelectedAndKeyboard) { + if (selection.length === 1 && !wasMultiSelectedAndKeyboard) { // Do not react if user is clicking on explorer items which are input placeholders if (!selection[0].name) { // Do not react if user is clicking on explorer items which are input placeholders From 01af251e6127ed6d8c5e14b814a5dba05a0dca33 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 17 Jan 2019 17:10:44 +0100 Subject: [PATCH 0345/1066] node-debug@1.31.1 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index b4b5bcdfb87..432ad403ea5 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -1,7 +1,7 @@ [ { "name": "ms-vscode.node-debug", - "version": "1.30.4", + "version": "1.31.1", "repo": "https://github.com/Microsoft/vscode-node-debug", "metadata": { "id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6", From 4f74a4339a42a8605fac95013b1a26ef50f20765 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:24:49 +0100 Subject: [PATCH 0346/1066] FuzzyScore.isDefault --- src/vs/base/common/filters.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index f0df6a97776..d8ad18872d4 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -505,6 +505,10 @@ export namespace FuzzyScore { * No matches and value `-100` */ export const Default: [-100, 0, 0] = [-100, 0, 0]; + + export function isDefault(score: FuzzyScore): score is [-100, 0, 0] { + return score[0] === -100 && score[1] === 0 && score[2] === 0; + } } export interface FuzzyScorer { From 3de2f5d05c97980639643fa45d0c1af14f01ad29 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:25:09 +0100 Subject: [PATCH 0347/1066] list commands: cleanup calls into tree --- src/vs/workbench/electron-browser/commands.ts | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 2d93dad4922..2123b8bb2d6 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -62,14 +62,14 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; + const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusNext(count, false, fakeKeyboardEvent); + tree.focusNext(count, true, fakeKeyboardEvent); - const listFocus = list.getFocus(); + const listFocus = tree.getFocus(); if (listFocus.length) { - list.reveal(listFocus[0]); + tree.reveal(listFocus[0]); } } @@ -192,14 +192,14 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; + const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.focusPrevious(count, false, fakeKeyboardEvent); + tree.focusPrevious(count, true, fakeKeyboardEvent); - const listFocus = list.getFocus(); + const listFocus = tree.getFocus(); if (listFocus.length) { - list.reveal(listFocus[0]); + tree.reveal(listFocus[0]); } } @@ -464,16 +464,15 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const first = list.getFirstElementChild(null); - - if (!first) { - return; - } - + const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.setFocus([first], fakeKeyboardEvent); - list.reveal(first); + tree.focusFirst(fakeKeyboardEvent); + + const focus = tree.getFocus(); + + if (focus.length > 0) { + tree.reveal(focus[0]); + } } // Tree @@ -517,16 +516,15 @@ export function registerCommands(): void { // ObjectTree else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { - const list = focused; - const last = list.getLastElementAncestor(); - - if (!last) { - return; - } - + const tree = focused; const fakeKeyboardEvent = new KeyboardEvent('keydown'); - list.setFocus([last], fakeKeyboardEvent); - list.reveal(last); + tree.focusLast(fakeKeyboardEvent); + + const focus = tree.getFocus(); + + if (focus.length > 0) { + tree.reveal(focus[0]); + } } // Tree From 9bab4c5ab6b8833fc834a38b69a18f66d4890569 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:25:25 +0100 Subject: [PATCH 0348/1066] refactor list focus navigation --- src/vs/base/browser/ui/list/listWidget.ts | 99 +++++++++++++++++++---- 1 file changed, 82 insertions(+), 17 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 2f0c0312532..1b8c5798fcc 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1228,41 +1228,54 @@ export class List implements ISpliceable, IDisposable { this.focus.set(indexes, browserEvent); } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { + focusNext(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } + const focus = this.focus.get(); - let index = focus.length > 0 ? focus[0] + n : 0; - this.setFocus(loop ? [index % this.length] : [Math.min(index, this.length - 1)], browserEvent); + const index = this.findNextIndex(focus.length > 0 ? focus[0] + n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } + const focus = this.focus.get(); - let index = focus.length > 0 ? focus[0] - n : 0; - if (loop && index < 0) { index = (this.length + (index % this.length)) % this.length; } - this.setFocus([Math.max(index, 0)], browserEvent); + const index = this.findPreviousIndex(focus.length > 0 ? focus[0] - n : 0, loop, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusNextPage(browserEvent?: UIEvent): void { + focusNextPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let lastPageIndex = this.view.indexAt(this.view.getScrollTop() + this.view.renderHeight); lastPageIndex = lastPageIndex === 0 ? 0 : lastPageIndex - 1; const lastPageElement = this.view.element(lastPageIndex); const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== lastPageElement) { - this.setFocus([lastPageIndex], browserEvent); + const lastGoodPageIndex = this.findPreviousIndex(lastPageIndex, false, filter); + + if (lastGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(lastGoodPageIndex)) { + this.setFocus([lastGoodPageIndex], browserEvent); + } else { + this.setFocus([lastPageIndex], browserEvent); + } } else { const previousScrollTop = this.view.getScrollTop(); this.view.setScrollTop(previousScrollTop + this.view.renderHeight - this.view.elementHeight(lastPageIndex)); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusNextPage(browserEvent), 0); + setTimeout(() => this.focusNextPage(browserEvent, filter), 0); } } } - focusPreviousPage(browserEvent?: UIEvent): void { + focusPreviousPage(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { let firstPageIndex: number; const scrollTop = this.view.getScrollTop(); @@ -1276,26 +1289,78 @@ export class List implements ISpliceable, IDisposable { const currentlyFocusedElement = this.getFocusedElements()[0]; if (currentlyFocusedElement !== firstPageElement) { - this.setFocus([firstPageIndex], browserEvent); + const firstGoodPageIndex = this.findNextIndex(firstPageIndex, false, filter); + + if (firstGoodPageIndex > -1 && currentlyFocusedElement !== this.view.element(firstGoodPageIndex)) { + this.setFocus([firstGoodPageIndex], browserEvent); + } else { + this.setFocus([firstPageIndex], browserEvent); + } } else { const previousScrollTop = scrollTop; this.view.setScrollTop(scrollTop - this.view.renderHeight); if (this.view.getScrollTop() !== previousScrollTop) { // Let the scroll event listener run - setTimeout(() => this.focusPreviousPage(browserEvent), 0); + setTimeout(() => this.focusPreviousPage(browserEvent, filter), 0); } } } - focusLast(browserEvent?: UIEvent): void { + focusLast(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([this.length - 1], browserEvent); + + const index = this.findPreviousIndex(this.length - 1, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } } - focusFirst(browserEvent?: UIEvent): void { + focusFirst(browserEvent?: UIEvent, filter?: (element: T) => boolean): void { if (this.length === 0) { return; } - this.setFocus([0], browserEvent); + + const index = this.findNextIndex(0, false, filter); + + if (index > -1) { + this.setFocus([index], browserEvent); + } + } + + private findNextIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index >= this.length && !loop) { + return -1; + } + + index = index % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index++; + } + + return -1; + } + + private findPreviousIndex(index: number, loop = false, filter?: (element: T) => boolean): number { + for (let i = 0; i < this.length; i++) { + if (index < 0 && !loop) { + return -1; + } + + index = (this.length + (index % this.length)) % this.length; + + if (!filter || filter(this.element(index))) { + return index; + } + + index--; + } + + return -1; } getFocus(): number[] { From 5d10be0bbd494192d05eef087208e4290353a688 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:25:56 +0100 Subject: [PATCH 0349/1066] tree: remove tree.getLastElementAncestor --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 ---- src/vs/base/browser/ui/tree/asyncDataTree.ts | 6 ------ 2 files changed, 10 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 20af5997ee3..3274e744efd 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -540,10 +540,6 @@ export abstract class AbstractTree implements IDisposable return this.model.getFirstElementChild(location); } - getLastElementAncestor(location?: TRef): T | undefined { - return this.model.getLastElementAncestor(location); - } - // Tree getNode(location?: TRef): ITreeNode { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 405c4161d54..409775946e0 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -517,12 +517,6 @@ export class AsyncDataTree implements IDisposable return (node && node.element)!; } - getLastElementAncestor(element: TInput | T = this.root.element): TInput | T | undefined { - const dataNode = this.getDataNode(element); - const node = this.tree.getLastElementAncestor(dataNode === this.root ? null : dataNode); - return (node && node.element)!; - } - // List get visibleNodeCount(): number { From 76094070969fd0a37e706e4ff6f561ef15ed1211 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:26:16 +0100 Subject: [PATCH 0350/1066] tree: focus navigation on type filters --- src/vs/base/browser/ui/tree/abstractTree.ts | 29 ++++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3274e744efd..fad76f7a2d4 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -8,7 +8,7 @@ import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, timeout } from 'vs/base/browser/dom'; -import { Event, Relay } from 'vs/base/common/event'; +import { Event, Relay, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; @@ -321,7 +321,15 @@ class TypeFilter implements ITreeFilter { class TypeFilterController implements IDisposable { + get pattern(): string { + return this._pattern; + } + + private _onDidChangePattern = new Emitter(); + readonly onDidChangePattern = this._onDidChangePattern.event; + private domNode: HTMLElement; + private _pattern = ''; private disposables: IDisposable[] = []; constructor( @@ -356,9 +364,12 @@ class TypeFilterController implements IDisposable { } private onInput(pattern: string): void { + this._pattern = pattern; this.domNode.textContent = pattern; this.filter.pattern = pattern; this.tree.refilter(); + + this._onDidChangePattern.fire(pattern); } dispose() { @@ -409,6 +420,7 @@ export abstract class AbstractTree implements IDisposable private view: List>; private renderers: TreeRenderer[]; + private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; protected disposables: IDisposable[] = []; @@ -470,7 +482,10 @@ export abstract class AbstractTree implements IDisposable if (options.keyboardNavigationLabelProvider) { const typeFilterController = new TypeFilterController(this, 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); + + typeFilterController.onDidChangePattern(() => this.focusNext(0, true), this, this.disposables); } } @@ -594,27 +609,27 @@ export abstract class AbstractTree implements IDisposable } focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusNext(n, loop, browserEvent); + this.view.focusNext(n, loop, browserEvent, this.focusNavigationFilter); } focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusPrevious(n, loop, browserEvent); + this.view.focusPrevious(n, loop, browserEvent, this.focusNavigationFilter); } focusNextPage(browserEvent?: UIEvent): void { - this.view.focusNextPage(browserEvent); + this.view.focusNextPage(browserEvent, this.focusNavigationFilter); } focusPreviousPage(browserEvent?: UIEvent): void { - this.view.focusPreviousPage(browserEvent); + this.view.focusPreviousPage(browserEvent, this.focusNavigationFilter); } focusLast(browserEvent?: UIEvent): void { - this.view.focusLast(browserEvent); + this.view.focusLast(browserEvent, this.focusNavigationFilter); } focusFirst(browserEvent?: UIEvent): void { - this.view.focusFirst(browserEvent); + this.view.focusFirst(browserEvent, this.focusNavigationFilter); } getFocus(): T[] { From 5a41fdd88fa6ed36dc1f78d0d2bd4ddd3aa471a8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Jan 2019 17:26:50 +0100 Subject: [PATCH 0351/1066] :lipstick: --- src/vs/workbench/parts/search/browser/searchView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 6a07b29ff17..bc43a26e383 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -1649,7 +1649,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { } private onFilesChanged(e: FileChangesEvent): void { - if (!this.viewModel) { + if (!this.viewModel || !e.gotDeleted()) { return; } From 35c95d420998cdff31be719a22702e168471435c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:51:07 +0100 Subject: [PATCH 0352/1066] list: use styles --- src/vs/base/browser/ui/list/list.css | 21 +++++++++++++++++++++ src/vs/base/browser/ui/list/listWidget.ts | 10 ++++++++++ src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/browser/ui/tree/media/tree.css | 14 -------------- src/vs/platform/theme/common/styler.ts | 8 ++++++-- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 55038eb39b6..b895487c96d 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -61,4 +61,25 @@ border-radius: 10px; font-size: 12px; position: absolute; +} + +/* Type filter */ +.monaco-list-type-filter { + position: absolute; + right: 5px; + top: 4px; + border-radius: 2px; + border-width: 1px; + border-style: solid; + padding: 0px 3px; + max-width: calc(100% - 10px); + text-overflow: ellipsis; + overflow: hidden; + text-align: right; + box-sizing: border-box; + direction: rtl; +} + +.monaco-list-type-filter:empty { + display: none; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 1b8c5798fcc..00f8d2e52ef 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -720,6 +720,14 @@ export class DefaultStyleController implements IStyleController { `); } + if (styles.listMatchesBackground) { + content.push(`.monaco-list-type-filter { background-color: ${styles.listMatchesBackground} }`); + } + + if (styles.listMatchesOutline) { + content.push(`.monaco-list-type-filter { border-color: ${styles.listMatchesOutline} }`); + } + const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { this.styleElement.innerHTML = newStyles; @@ -764,6 +772,8 @@ export interface IListStyles { listInactiveFocusOutline?: Color; listSelectionOutline?: Color; listHoverOutline?: Color; + listMatchesBackground?: Color; + listMatchesOutline?: Color; } const defaultStyles: IListStyles = { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index fad76f7a2d4..10a8a023265 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -339,7 +339,7 @@ class TypeFilterController implements IDisposable { keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider ) { const container = view.getHTMLElement(); - this.domNode = append(container, $('.monaco-tl-type-filter')); + this.domNode = append(container, $('.monaco-list-type-filter')); const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onInput = Event.chain(domEvent(container, 'keydown')) diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index 48d2eced3e8..aa46437ecd7 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -65,18 +65,4 @@ .hc-black .monaco-tl-twistie.loading { background-image: url("loading-hc.svg"); -} - -.monaco-tl-type-filter { - position: absolute; - right: 5px; - top: 4px; - border-radius: 2px; - background: #0947718a; /* TODO@joao: theme */ - border: 1px solid #8080808a; - padding: 0px 3px; -} - -.monaco-tl-type-filter:empty { - display: none; } \ No newline at end of file diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index dc424253896..957ecc48ef6 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, editorFindMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -223,6 +223,8 @@ export interface IListStyleOverrides extends IStyleOverrides { listInactiveFocusOutline?: ColorIdentifier; listSelectionOutline?: ColorIdentifier; listHoverOutline?: ColorIdentifier; + listMatchesBackground?: ColorIdentifier; + listMatchesOutline?: ColorIdentifier; } export function attachListStyler(widget: IThemable, themeService: IThemeService, overrides?: IListStyleOverrides): IDisposable { @@ -244,7 +246,9 @@ export const defaultListStyles: IColorMapping = { listDropBackground: listDropBackground, listFocusOutline: activeContrastBorder, listSelectionOutline: activeContrastBorder, - listHoverOutline: activeContrastBorder + listHoverOutline: activeContrastBorder, + listMatchesBackground: editorFindMatchHighlight, + listMatchesOutline: editorFindMatchHighlightBorder }; export interface IButtonStyleOverrides extends IStyleOverrides { From 63c71c6b8c52291ec23a0a8cd3be15730ff6e971 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 17:55:41 +0100 Subject: [PATCH 0353/1066] fixes #66677 --- src/vs/base/common/filters.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index f0df6a97776..3b128cddf7b 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -386,7 +386,13 @@ export function createMatches(score: undefined | FuzzyScore): IMatch[] { if (mask > matches) { break; } else if (matches & mask) { - res.push({ start: pos, end: pos + 1 }); + const last = res[res.length - 1]; + + if (last && last.end === pos) { + last.end = pos + 1; + } else { + res.push({ start: pos, end: pos + 1 }); + } } } return res; From 7bd16e21c89ea66f839e12e778a63e86c1bdb403 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 18:04:37 +0100 Subject: [PATCH 0354/1066] label service new structure for formatters --- .../standalone/browser/simpleServices.ts | 4 +- src/vs/platform/label/common/label.ts | 26 ++-- .../electron-browser/mainThreadFileSystem.ts | 6 +- src/vs/workbench/api/node/extHost.protocol.ts | 4 +- .../workbench/api/node/extHostFileSystem.ts | 6 +- .../electron-browser/files.contribution.ts | 11 +- .../services/label/common/labelService.ts | 122 +++++++++--------- .../services/label/test/label.test.ts | 10 +- 8 files changed, 94 insertions(+), 95 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index e58fb45a8c8..c8247a83871 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -36,7 +36,7 @@ import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingReso import { IKeybindingItem, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; -import { ILabelService, LabelRules } from 'vs/platform/label/common/label'; +import { ILabelService, ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { INotification, INotificationHandle, INotificationService, IPromptChoice, IPromptOptions, NoOpNotification } from 'vs/platform/notification/common/notification'; import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress'; import { ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -625,7 +625,7 @@ export class SimpleUriLabelService implements ILabelService { return ''; } - public registerFormatter(selector: string, formatter: LabelRules): IDisposable { + public registerFormatter(formatter: ResourceLabelFormatter): IDisposable { throw new Error('Not implemented'); } diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 8e81be2bf4e..5846fd3796e 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -25,21 +25,23 @@ export interface ILabelService { getUriLabel(resource: URI, options?: { relative?: boolean, noPrefix?: boolean }): string; getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(): string; - registerFormatter(selector: string, formatter: LabelRules): IDisposable; + registerFormatter(formatter: ResourceLabelFormatter): IDisposable; onDidRegisterFormatter: Event; } -export interface LabelRules { - uri: { - label: string; // myLabel:/${path} - separator: '/' | '\\' | ''; - tildify?: boolean; - normalizeDriveLetter?: boolean; - authorityPrefix?: string; - }; - workspace?: { - suffix: string; - }; +export interface ResourceLabelFormatter { + scheme: string; + authority?: string; + formatting: ResourceLabelFormatting; +} + +export interface ResourceLabelFormatting { + label: string; // myLabel:/${path} + separator: '/' | '\\' | ''; + tildify?: boolean; + normalizeDriveLetter?: boolean; + workspaceSuffix?: string; + authorityPrefix?: string; } const LABEL_SERVICE_ID = 'label'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 277b711619d..3ad9bff063c 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -9,7 +9,7 @@ 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 { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostFileSystemShape, IExtHostContext, IFileChangeDto, MainContext, MainThreadFileSystemShape } from '../node/extHost.protocol'; -import { LabelRules, ILabelService } from 'vs/platform/label/common/label'; +import { ResourceLabelFormatter, ILabelService } from 'vs/platform/label/common/label'; @extHostNamedCustomer(MainContext.MainThreadFileSystem) export class MainThreadFileSystem implements MainThreadFileSystemShape { @@ -39,8 +39,8 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { this._fileProvider.delete(handle); } - $setUriFormatter(selector: string, formatter: LabelRules): void { - this._labelService.registerFormatter(selector, formatter); + $setUriFormatter(formatter: ResourceLabelFormatter): void { + this._labelService.registerFormatter(formatter); } $onFileSystemChange(handle: number, changes: IFileChangeDto[]): void { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7ecd69fcb54..fdaa10614ad 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -21,7 +21,7 @@ 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 { LabelRules } from 'vs/platform/label/common/label'; +import { ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { LogLevel } from 'vs/platform/log/common/log'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IPickOptions, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; @@ -512,7 +512,7 @@ export interface IFileChangeDto { export interface MainThreadFileSystemShape extends IDisposable { $registerFileSystemProvider(handle: number, scheme: string, capabilities: FileSystemProviderCapabilities): void; $unregisterProvider(handle: number): void; - $setUriFormatter(scheme: string, formatter: LabelRules): void; + $setUriFormatter(formatter: ResourceLabelFormatter): void; $onFileSystemChange(handle: number, resource: IFileChangeDto[]): void; } diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 6a605ba9158..2fd8bd78205 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -12,7 +12,7 @@ import { FileChangeType, DocumentLink } from 'vs/workbench/api/node/extHostTypes import * as typeConverter from 'vs/workbench/api/node/extHostTypeConverters'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/node/extHostLanguageFeatures'; import { Schemas } from 'vs/base/common/network'; -import { LabelRules } from 'vs/platform/label/common/label'; +import { ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { State, StateMachine, LinkComputer } from 'vs/editor/common/modes/linkComputer'; import { commonPrefixLength } from 'vs/base/common/strings'; import { CharCode } from 'vs/base/common/charCode'; @@ -205,8 +205,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { }); } - setUriFormatter(scheme: string, formatter: LabelRules): void { - this._proxy.$setUriFormatter(scheme, formatter); + setUriFormatter(formatter: ResourceLabelFormatter): void { + this._proxy.$setUriFormatter(formatter); } private static _asIStat(stat: vscode.FileStat): files.IStat { diff --git a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts index b325009c33e..a0bfe75ae60 100644 --- a/src/vs/workbench/parts/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/files.contribution.ts @@ -57,16 +57,15 @@ export class OpenExplorerViewletAction extends ShowViewletAction { class FileUriLabelContribution implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { - labelService.registerFormatter('file://', { - uri: { + labelService.registerFormatter({ + scheme: 'file', + formatting: { label: '${authority}${path}', separator: nativeSep, tildify: !platform.isWindows, normalizeDriveLetter: platform.isWindows, - authorityPrefix: nativeSep + nativeSep - }, - workspace: { - suffix: '' + authorityPrefix: nativeSep + nativeSep, + workspaceSuffix: '' } }); } diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 56d994214dc..2e7fc21a2a6 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -12,65 +12,56 @@ import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/comm import { isEqual, basenameOrAuthority, basename as resourceBasename } from 'vs/base/common/resources'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { tildify, getPathLabel } from 'vs/base/common/labels'; -import { ltrim, startsWith } from 'vs/base/common/strings'; +import { ltrim } from 'vs/base/common/strings'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION, toWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { isParent } from 'vs/platform/files/common/files'; import { basename, dirname, join } from 'vs/base/common/paths'; import { Schemas } from 'vs/base/common/network'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; -import { ILabelService, LabelRules } from 'vs/platform/label/common/label'; +import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting } from 'vs/platform/label/common/label'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { match } from 'vs/base/common/glob'; -interface LabelRulesWithPrefix extends LabelRules { - prefix: string; -} - -export const labelsExtPoint = ExtensionsRegistry.registerExtensionPoint({ - extensionPoint: 'labels', +export const labelsExtPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'resourceLabelFormatters', jsonSchema: { - description: localize('vscode.extension.contributes.labels', 'Contributes label formatting rules.'), + description: localize('vscode.extension.contributes.resourceLabelFormatters', 'Contributes resource label formatting rules.'), type: 'array', items: { type: 'object', - required: ['prefix'], + required: ['scheme', 'formatting'], properties: { - prefix: { + scheme: { type: 'string', - description: localize('vscode.extension.contributes.labels.prefix', 'URI prefix on which to match the rules on. For example file:///myFolder'), + description: localize('vscode.extension.contributes.resourceLabelFormatters.scheme', 'URI scheme on which to match the formatter on. For example "file". Simple glob patterns are supported.'), }, - uri: { - description: localize('vscode.extension.contributes.labels.uri', "Rules for formatting uri resource labels."), + authority: { + type: 'string', + description: localize('vscode.extension.contributes.resourceLabelFormatters.authority', 'URI authority on which to match the formatter on. Simple glob patterns are supported.'), + }, + formatting: { + description: localize('vscode.extension.contributes.resourceLabelFormatters.formatting', "Rules for formatting uri resource labels."), type: 'object', properties: { label: { type: 'string', - description: localize('vscode.extension.contributes.labels.uri.label', "Label rules to display. For example: myLabel:/${path}. ${path}, ${scheme} and ${authority} are supported as variables.") + description: localize('vscode.extension.contributes.resourceLabelFormatters.label', "Label rules to display. For example: myLabel:/${path}. ${path}, ${scheme} and ${authority} are supported as variables.") }, separator: { type: 'string', - description: localize('vscode.extension.contributes.labels.uri.separator', "Separator to be used in the uri label display. '/' or '\' as an example.") + description: localize('vscode.extension.contributes.resourceLabelFormatters.separator', "Separator to be used in the uri label display. '/' or '\' as an example.") }, tildify: { type: 'boolean', - description: localize('vscode.extension.contributes.labels.uri.tildify', "Controls if the start of the uri label should be tildified when possible.") + description: localize('vscode.extension.contributes.resourceLabelFormatters.tildify', "Controls if the start of the uri label should be tildified when possible.") }, - normalizeDriveLetter: { - type: 'boolean', - description: localize('vscode.extension.contributes.labels.uri.normalizeDriveLetter', "Controls if the drive letters should be upper cased.") - } - } - }, - workspace: { - description: localize('vscode.extension.contributes.labels.workspace', "Rules for formatting workspace labels."), - type: 'object', - properties: { - suffix: { + workspaceSuffix: { type: 'string', - description: localize('vscode.extension.contributes.labels.workspace.suffix', "Suffix appended to the workspace label.") + description: localize('vscode.extension.contributes.resourceLabelFormatters.formatting.workspaceSuffix', "Suffix appended to the workspace label.") } } - }, + } } } } @@ -86,7 +77,7 @@ function hasDriveLetter(path: string): boolean { export class LabelService implements ILabelService { _serviceBrand: any; - private readonly formatters: { [prefix: string]: LabelRules } = Object.create(null); + private formatters: ResourceLabelFormatter[] = []; private readonly _onDidRegisterFormatter = new Emitter(); constructor( @@ -95,7 +86,7 @@ export class LabelService implements ILabelService { @IWindowService private readonly windowService: IWindowService ) { labelsExtPoint.setHandler(extensions => { - extensions.forEach(extension => extension.value.forEach(rule => this.formatters[rule.prefix] = rule)); + extensions.forEach(extension => extension.value.forEach(formatter => this.formatters.push(formatter))); this._onDidRegisterFormatter.fire(); }); } @@ -104,23 +95,28 @@ export class LabelService implements ILabelService { return this._onDidRegisterFormatter.event; } - findFormatter(resource: URI): LabelRules | undefined { - const path = `${resource.scheme}://${resource.authority}`; - let bestPrefix = ''; - for (let prefix in this.formatters) { - if (startsWith(path, prefix) && prefix.length > bestPrefix.length) { - bestPrefix = prefix; + findFormatting(resource: URI): ResourceLabelFormatting | undefined { + let bestResult: ResourceLabelFormatter; + + this.formatters.forEach(formatter => { + if (formatter.scheme === resource.scheme) { + if (!formatter.authority && !bestResult) { + bestResult = formatter; + return; + } + + if (match(resource.authority, formatter.authority) && (!bestResult.authority || formatter.authority.length > bestResult.authority.length)) { + bestResult = formatter; + } } - } - if (bestPrefix.length) { - return this.formatters[bestPrefix]; - } - return undefined; + }); + + return bestResult.formatting; } getUriLabel(resource: URI, options: { relative?: boolean, noPrefix?: boolean } = {}): string { - const formatter = this.findFormatter(resource); - if (!formatter) { + const formatting = this.findFormatting(resource); + if (!formatting) { return getPathLabel(resource.path, this.environmentService, options.relative ? this.contextService : undefined); } @@ -131,8 +127,8 @@ export class LabelService implements ILabelService { if (isEqual(baseResource.uri, resource, !isLinux)) { relativeLabel = ''; // no label if resources are identical } else { - const baseResourceLabel = this.formatUri(baseResource.uri, formatter, options.noPrefix); - relativeLabel = ltrim(this.formatUri(resource, formatter, options.noPrefix).substring(baseResourceLabel.length), formatter.uri.separator); + const baseResourceLabel = this.formatUri(baseResource.uri, formatting, options.noPrefix); + relativeLabel = ltrim(this.formatUri(resource, formatting, options.noPrefix).substring(baseResourceLabel.length), formatting.separator); } const hasMultipleRoots = this.contextService.getWorkspace().folders.length > 1; @@ -145,7 +141,7 @@ export class LabelService implements ILabelService { } } - return this.formatUri(resource, formatter, options.noPrefix); + return this.formatUri(resource, formatting, options.noPrefix); } getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string { @@ -166,8 +162,8 @@ export class LabelService implements ILabelService { return label; } - const formatter = this.findFormatter(workspace); - const suffix = formatter && formatter.workspace && (typeof formatter.workspace.suffix === 'string') ? formatter.workspace.suffix : workspace.scheme; + const formatting = this.findFormatting(workspace); + const suffix = formatting && (typeof formatting.workspaceSuffix === 'string') ? formatting.workspaceSuffix : workspace.scheme; return suffix ? `${label} (${suffix})` : label; } @@ -190,26 +186,26 @@ export class LabelService implements ILabelService { if (this.windowService) { const authority = this.windowService.getConfiguration().remoteAuthority; if (authority) { - const formatter = this.findFormatter(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); - if (formatter && formatter.workspace) { - return formatter.workspace.suffix; + const formatter = this.findFormatting(URI.from({ scheme: REMOTE_HOST_SCHEME, authority })); + if (formatter && formatter.workspaceSuffix) { + return formatter.workspaceSuffix; } } } return ''; } - registerFormatter(selector: string, formatter: LabelRules): IDisposable { - this.formatters[selector] = formatter; + registerFormatter(formatter: ResourceLabelFormatter): IDisposable { + this.formatters.push(formatter); this._onDidRegisterFormatter.fire(); return { - dispose: () => delete this.formatters[selector] + dispose: () => this.formatters = this.formatters.filter(f => f !== formatter) }; } - private formatUri(resource: URI, formatter: LabelRules, forceNoTildify?: boolean): string { - let label = formatter.uri.label.replace(labelMatchingRegexp, match => { + private formatUri(resource: URI, formatting: ResourceLabelFormatting, forceNoTildify?: boolean): string { + let label = formatting.label.replace(labelMatchingRegexp, match => { switch (match) { case '${scheme}': return resource.scheme; case '${authority}': return resource.authority; @@ -219,17 +215,17 @@ export class LabelService implements ILabelService { }); // convert \c:\something => C:\something - if (formatter.uri.normalizeDriveLetter && hasDriveLetter(label)) { + if (formatting.normalizeDriveLetter && hasDriveLetter(label)) { label = label.charAt(1).toUpperCase() + label.substr(2); } - if (formatter.uri.tildify && !forceNoTildify) { + if (formatting.tildify && !forceNoTildify) { label = tildify(label, this.environmentService.userHome); } - if (formatter.uri.authorityPrefix && resource.authority) { - label = formatter.uri.authorityPrefix + label; + if (formatting.authorityPrefix && resource.authority) { + label = formatting.authorityPrefix + label; } - return label.replace(sepRegexp, formatter.uri.separator); + return label.replace(sepRegexp, formatting.separator); } } diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index f983ba30f34..39a59ecf86c 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -20,8 +20,9 @@ suite('URI Label', () => { }); test('file scheme', function () { - labelService.registerFormatter('file://', { - uri: { + labelService.registerFormatter({ + scheme: 'file://', + formatting: { label: '${path}', separator: nativeSep, tildify: !isWindows, @@ -38,8 +39,9 @@ suite('URI Label', () => { }); test('custom scheme', function () { - labelService.registerFormatter('vscode://', { - uri: { + labelService.registerFormatter({ + scheme: 'vscode://', + formatting: { label: 'LABEL/${path}/${authority}/END', separator: '/', tildify: true, From 30e72475b49db5bf54ec3566a1007e2c78fc4a6c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 18:08:30 +0100 Subject: [PATCH 0355/1066] fix FuzzyScore.isDefault --- src/vs/base/common/filters.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index ab29a7a7780..b89aedef0e9 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -512,8 +512,8 @@ export namespace FuzzyScore { */ export const Default: [-100, 0, 0] = [-100, 0, 0]; - export function isDefault(score: FuzzyScore): score is [-100, 0, 0] { - return score[0] === -100 && score[1] === 0 && score[2] === 0; + export function isDefault(score?: FuzzyScore): score is [-100, 0, 0] { + return !score || (score[0] === -100 && score[1] === 0 && score[2] === 0); } } From 311fa7e8b0d06bebeeef54b2ed486c44bfa7415d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Jan 2019 09:11:14 -0800 Subject: [PATCH 0356/1066] debt - don't send startup times twice --- .../performance/electron-browser/startupTimings.ts | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index c8ea72f9518..949e0d28051 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -41,11 +41,11 @@ export class StartupTimings implements IWorkbenchContribution { private async _report() { const isStandardStartup = await this._isStandardStartup(); - this._reportStartupTimes(isStandardStartup).catch(onUnexpectedError); + this._reportStartupTimes().catch(onUnexpectedError); this._appendStartupTimes(isStandardStartup).catch(onUnexpectedError); } - private async _reportStartupTimes(isStandardStartup: boolean): Promise { + private async _reportStartupTimes(): Promise { const metrics = await this._timerService.startupMetrics; /* __GDPR__ @@ -56,15 +56,6 @@ export class StartupTimings implements IWorkbenchContribution { } */ this._telemetryService.publicLog('startupTimeVaried', metrics); - - /* __GDPR__ - "startupTime" : { - "${include}": [ - "${IStartupMetrics}" - ] - } - */ - this._telemetryService.publicLog('startupTime', metrics); } private async _appendStartupTimes(isStandardStartup: boolean) { From f39b41b6b4df9c98deee79bff723babb31ac66fb Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 18:12:01 +0100 Subject: [PATCH 0357/1066] set default --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 10a8a023265..9d578fceb6f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -306,13 +306,13 @@ class TypeFilter implements ITreeFilter { if (!score) { // DEMO: just highlights - return { data: FuzzyScore.Default, visibility: true }; + // return { data: FuzzyScore.Default, visibility: true }; // DEMO: filter // return TreeVisibility.Recurse; // DEMO: smarter filter - // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } return { data: score, visibility: true }; From 218d538442052f2a16ca0871cf4f18bed69805ad Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 17 Jan 2019 18:12:08 +0100 Subject: [PATCH 0358/1066] remove rtl --- src/vs/base/browser/ui/list/list.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index b895487c96d..61bd0dbf52c 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -77,7 +77,6 @@ overflow: hidden; text-align: right; box-sizing: border-box; - direction: rtl; } .monaco-list-type-filter:empty { From 1ec1e2f8cb1f870c07748c8dd36e6d3145e89845 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 17 Jan 2019 17:30:29 +0000 Subject: [PATCH 0359/1066] Fix unnecessary character in strings.escape regex --- src/vs/base/common/strings.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index e46f0ee3937..39da4f5446f 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -56,7 +56,7 @@ export function format(value: string, ...args: any[]): string { * being used e.g. in HTMLElement.innerHTML. */ export function escape(html: string): string { - return html.replace(/[<|>|&]/g, function (match) { + return html.replace(/[<>&]/g, function (match) { switch (match) { case '<': return '<'; case '>': return '>'; From 76bfcb0f8108df7b011f7d82283f7c3376646046 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 17 Jan 2019 18:32:11 +0100 Subject: [PATCH 0360/1066] label: introduce ResourceLabelFormattersHandler --- .../services/label/common/labelService.ts | 31 +++++++++----- .../services/label/test/label.test.ts | 40 ++++++++++++++++++- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 2e7fc21a2a6..f6b4481909a 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -7,6 +7,8 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceContextService, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { isEqual, basenameOrAuthority, basename as resourceBasename } from 'vs/base/common/resources'; @@ -22,8 +24,9 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { ILabelService, ResourceLabelFormatter, ResourceLabelFormatting } from 'vs/platform/label/common/label'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { match } from 'vs/base/common/glob'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -export const labelsExtPoint = ExtensionsRegistry.registerExtensionPoint({ +const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', jsonSchema: { description: localize('vscode.extension.contributes.resourceLabelFormatters', 'Contributes resource label formatting rules.'), @@ -74,6 +77,16 @@ function hasDriveLetter(path: string): boolean { return !!(isWindows && path && path[2] === ':'); } + +class ResourceLabelFormattersHandler implements IWorkbenchContribution { + constructor(@ILabelService labelService: ILabelService) { + resourceLabelFormattersExtPoint.setHandler(extensions => { + extensions.forEach(extension => extension.value.forEach(formatter => labelService.registerFormatter(formatter))); + }); + } +} +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ResourceLabelFormattersHandler, LifecyclePhase.Restored); + export class LabelService implements ILabelService { _serviceBrand: any; @@ -84,12 +97,7 @@ export class LabelService implements ILabelService { @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IWindowService private readonly windowService: IWindowService - ) { - labelsExtPoint.setHandler(extensions => { - extensions.forEach(extension => extension.value.forEach(formatter => this.formatters.push(formatter))); - this._onDidRegisterFormatter.fire(); - }); - } + ) { } get onDidRegisterFormatter(): Event { return this._onDidRegisterFormatter.event; @@ -100,18 +108,21 @@ export class LabelService implements ILabelService { this.formatters.forEach(formatter => { if (formatter.scheme === resource.scheme) { - if (!formatter.authority && !bestResult) { + if (!bestResult) { bestResult = formatter; return; } + if (!formatter.authority) { + return; + } - if (match(resource.authority, formatter.authority) && (!bestResult.authority || formatter.authority.length > bestResult.authority.length)) { + if (match(formatter.authority, resource.authority) && (!bestResult.authority || formatter.authority.length > bestResult.authority.length)) { bestResult = formatter; } } }); - return bestResult.formatting; + return bestResult ? bestResult.formatting : undefined; } getUriLabel(resource: URI, options: { relative?: boolean, noPrefix?: boolean } = {}): string { diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index 39a59ecf86c..fb20a96b09b 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -21,7 +21,7 @@ suite('URI Label', () => { test('file scheme', function () { labelService.registerFormatter({ - scheme: 'file://', + scheme: 'file', formatting: { label: '${path}', separator: nativeSep, @@ -40,7 +40,7 @@ suite('URI Label', () => { test('custom scheme', function () { labelService.registerFormatter({ - scheme: 'vscode://', + scheme: 'vscode', formatting: { label: 'LABEL/${path}/${authority}/END', separator: '/', @@ -52,4 +52,40 @@ suite('URI Label', () => { const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END'); }); + + test('custom authority', function () { + labelService.registerFormatter({ + scheme: 'vscode', + authority: 'micro*', + formatting: { + label: 'LABEL/${path}/${authority}/END', + separator: '/' + } + }); + + const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); + assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END'); + }); + + test('mulitple authority', function () { + labelService.registerFormatter({ + scheme: 'vscode', + authority: 'micro*', + formatting: { + label: 'first', + separator: '/' + } + }); + labelService.registerFormatter({ + scheme: 'vscode', + authority: 'microsof*', + formatting: { + label: 'second', + separator: '/' + } + }); + + const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); + assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'second'); + }); }); From 41a6e73016b43875c61929b237bf5da978890901 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 17 Jan 2019 09:41:16 -0800 Subject: [PATCH 0361/1066] Fix regex replacing single quote --- .../parts/terminal/electron-browser/terminalInstance.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 598227eb4fc..3560ab6ebdd 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -802,7 +802,7 @@ export class TerminalInstance implements ITerminalInstance { this.title === 'powershell'; if (isPowerShell && (hasSpace || originalPath.indexOf('\'') !== -1)) { - c(`& '${originalPath.replace('\'', '\'\'')}'`); + c(`& '${originalPath.replace(/'/g, '\'\'')}'`); return; } From 35eae99ec3549f6658d6772920f89b63cb653b77 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 17 Jan 2019 18:05:57 +0000 Subject: [PATCH 0362/1066] Fix more lgtm.com issues --- extensions/emmet/src/abbreviationActions.ts | 4 ++-- src/vs/base/common/assert.ts | 2 +- src/vs/base/common/types.ts | 2 +- src/vs/workbench/parts/search/browser/openAnythingHandler.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/emmet/src/abbreviationActions.ts b/extensions/emmet/src/abbreviationActions.ts index dbde660d9e5..4ee8e221f99 100644 --- a/extensions/emmet/src/abbreviationActions.ts +++ b/extensions/emmet/src/abbreviationActions.ts @@ -7,8 +7,8 @@ import * as vscode from 'vscode'; import { Node, HtmlNode, Rule, Property, Stylesheet } from 'EmmetNode'; import { getEmmetHelper, getNode, getInnerRange, getMappingForIncludedLanguages, parseDocument, validate, getEmmetConfiguration, isStyleSheet, getEmmetMode, parsePartialStylesheet, isStyleAttribute, getEmbeddedCssNodeIfAny, allowedMimeTypesInScriptTag } from './util'; -const trimRegex = /[\u00a0]*[\d|#|\-|\*|\u2022]+\.?/; -const hexColorRegex = /^#[\d,a-f,A-F]{0,6}$/; +const trimRegex = /[\u00a0]*[\d#\-\*\u2022]+\.?/; +const hexColorRegex = /^#[\da-fA-F]{0,6}$/; const inlineElements = ['a', 'abbr', 'acronym', 'applet', 'b', 'basefont', 'bdo', 'big', 'br', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'map', 'object', 'q', diff --git a/src/vs/base/common/assert.ts b/src/vs/base/common/assert.ts index 0a54ee2e51b..1e227df640e 100644 --- a/src/vs/base/common/assert.ts +++ b/src/vs/base/common/assert.ts @@ -7,7 +7,7 @@ * Throws an error with the provided message if the provided value does not evaluate to a true Javascript value. */ export function ok(value?: any, message?: string) { - if (!value || value === null) { + if (!value) { throw new Error(message ? 'Assertion failed (' + message + ')' : 'Assertion Failed'); } } diff --git a/src/vs/base/common/types.ts b/src/vs/base/common/types.ts index 447ba503496..c05da11b423 100644 --- a/src/vs/base/common/types.ts +++ b/src/vs/base/common/types.ts @@ -124,7 +124,7 @@ export function isFunction(obj: any): obj is Function { * @returns whether the provided parameters is are JavaScript Function or not. */ export function areFunctions(...objects: any[]): boolean { - return objects && objects.length > 0 && objects.every(isFunction); + return objects.length > 0 && objects.every(isFunction); } export type TypeConstraint = string | Function; diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 06d934a7501..7ff26d8ff63 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -32,7 +32,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { static readonly ID = 'workbench.picker.anything'; - private static readonly LINE_COLON_PATTERN = /[#|:|\(](\d*)([#|:|,](\d*))?\)?$/; + private static readonly LINE_COLON_PATTERN = /[#:\(](\d*)([#:,](\d*))?\)?$/; private static readonly TYPING_SEARCH_DELAY = 200; // This delay accommodates for the user typing a word and then stops typing to start searching From 1539caf1b46d29feb79d1071a06e3c9c7fdb77de Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 17 Jan 2019 19:29:56 +0100 Subject: [PATCH 0363/1066] cleanup launchVSCode request --- .../parts/debug/common/debugUtils.ts | 14 ++-- .../debug/electron-browser/rawDebugSession.ts | 67 +++++-------------- 2 files changed, 24 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugUtils.ts b/src/vs/workbench/parts/debug/common/debugUtils.ts index a1effe8f738..88b3c6e2251 100644 --- a/src/vs/workbench/parts/debug/common/debugUtils.ts +++ b/src/vs/workbench/parts/debug/common/debugUtils.ts @@ -169,14 +169,12 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePaths: (toDA: fixSourcePaths(true, (request.arguments).source); break; case 'launchVSCode': - tmpSource.path = request.arguments.extensionDevelopmentPath; - fixSourcePaths(false, tmpSource); - request.arguments.extensionDevelopmentPath = tmpSource.path; - - request.arguments.pathArgs.forEach(p => { - tmpSource.path = p.path; - fixSourcePaths(false, tmpSource); - p.path = tmpSource.path; + request.arguments.args.forEach(a => { + if (a.path) { + tmpSource.path = a.path; + fixSourcePaths(false, tmpSource); + a.path = tmpSource.path; + } }); break; default: diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index bdd0199a367..0409ac76066 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -15,16 +15,18 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import * as cp from 'child_process'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +/** + * This interface represents a single command line argument split into a "prefix" and a "path" half. + * The optional "prefix" contains arbitrary text and the optional "path" contains a file system path. + * Concatenating both results in the original command line argument. + */ +export interface ILaunchVSCodeArgument { + prefix?: string; + path?: string; +} + export interface ILaunchVSCodeArguments { - sessionId: string; - debugMode: 'noDebug' | 'break' | 'noBreak'; - debugPort: number; - options: string[]; - extensionDevelopmentPath: string; - pathArgs: { - type: 'folder' | 'file'; - path: string; - }[]; + args: ILaunchVSCodeArgument[]; env?: { [key: string]: string | null; }; } @@ -542,41 +544,21 @@ export class RawDebugSession { } } - private launchVsCode(args: ILaunchVSCodeArguments): Promise { + private launchVsCode(vscodeArgs: ILaunchVSCodeArguments): Promise { const spawnOpts: cp.SpawnOptions = { detached: false // https://github.com/Microsoft/vscode/issues/57018 }; - if (args.env) { + if (vscodeArgs.env) { // merge environment variables into a copy of the process.env - const envArgs = objects.mixin(objects.mixin({}, process.env), args.env); + const envArgs = objects.mixin(objects.mixin({}, process.env), vscodeArgs.env); // and delete some if necessary Object.keys(envArgs).filter(k => envArgs[k] === null).forEach(key => delete envArgs[key]); spawnOpts.env = envArgs; } - let spawnArgs = [].concat(args.options); - - switch (args.debugMode) { - case 'noDebug': - break; - case 'break': - spawnArgs.push(`--inspect-brk-extensions=${args.debugPort}`); - break; - case 'noBreak': - spawnArgs.push(`--inspect-extensions=${args.debugPort}`); - break; - } - - // pass the debug session ID to the EH so that broadcast events know where they come from - if (args.sessionId) { - spawnArgs.push(`--debugId=${args.sessionId}`); - } - - if (args.extensionDevelopmentPath) { - spawnArgs.push(`--extensionDevelopmentPath=${args.extensionDevelopmentPath}`); - } + let spawnArgs = vscodeArgs.args.map(a => (a.prefix || '') + (a.path || '')); let runtimeExecutable = this.environmentService['execPath']; if (!runtimeExecutable) { @@ -590,25 +572,12 @@ export class RawDebugSession { const vscodeWorkspacePath = runtimeExecutable.substr(0, electronIdx); // only add path if user hasn't already added that path - if (args.pathArgs.length === 0 || args.pathArgs[0].path.indexOf(vscodeWorkspacePath) !== 0) { - spawnArgs.push(vscodeWorkspacePath); + const x = spawnArgs.filter(a => a.indexOf(vscodeWorkspacePath) >= 0); + if (x.length === 0) { + spawnArgs.unshift(vscodeWorkspacePath); } } - args.pathArgs.forEach(p => { - switch (p.type) { - case 'file': - spawnArgs.push(`--file-uri=${p.path}`); - break; - case 'folder': - spawnArgs.push(`--folder-uri=${p.path}`); - break; - default: - spawnArgs.push(p.path); - break; - } - }); - // Workaround for bug Microsoft/vscode#45832 if (process.platform === 'win32' && runtimeExecutable.indexOf(' ') > 0) { let foundArgWithSpace = false; From 54adadd72c4f74cf02539693ccee2702a7354a2d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Thu, 17 Jan 2019 20:53:18 +0100 Subject: [PATCH 0364/1066] fix lgtm warnings --- extensions/npm/src/features/bowerJSONContribution.ts | 2 +- extensions/npm/src/features/packageJSONContribution.ts | 4 ++-- src/vs/base/common/json.ts | 6 +++--- src/vs/code/electron-main/windows.ts | 3 --- .../services/themes/electron-browser/colorThemeData.ts | 5 ++++- .../themes/electron-browser/workbenchThemeService.ts | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/npm/src/features/bowerJSONContribution.ts b/extensions/npm/src/features/bowerJSONContribution.ts index 4770303760c..705e40d34e7 100644 --- a/extensions/npm/src/features/bowerJSONContribution.ts +++ b/extensions/npm/src/features/bowerJSONContribution.ts @@ -167,7 +167,7 @@ export class BowerJSONContribution implements IJSONContribution { if (url.indexOf('git://') === 0) { url = url.substring(6); } - if (url.lastIndexOf('.git') === url.length - 4) { + if (url.length >= 4 && url.substr(url.length - 4) === '.git') { url = url.substring(0, url.length - 4); } return url; diff --git a/extensions/npm/src/features/packageJSONContribution.ts b/extensions/npm/src/features/packageJSONContribution.ts index d667de3981f..88cf753425c 100644 --- a/extensions/npm/src/features/packageJSONContribution.ts +++ b/extensions/npm/src/features/packageJSONContribution.ts @@ -229,7 +229,7 @@ export class PackageJSONContribution implements IJSONContribution { if ((location.matches(['dependencies', '*']) || location.matches(['devDependencies', '*']) || location.matches(['optionalDependencies', '*']) || location.matches(['peerDependencies', '*']))) { const currentKey = location.path[location.path.length - 1]; if (typeof currentKey === 'string') { - const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(currentKey).replace('%40', '@'); + const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(currentKey).replace(/%40/g, '@'); return this.xhr({ url: queryUrl, agent: USER_AGENT @@ -289,7 +289,7 @@ export class PackageJSONContribution implements IJSONContribution { private getInfo(pack: string): Thenable { - const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace('%40', '@'); + const queryUrl = 'https://registry.npmjs.org/' + encodeURIComponent(pack).replace(/%40/g, '@'); return this.xhr({ url: queryUrl, agent: USER_AGENT diff --git a/src/vs/base/common/json.ts b/src/vs/base/common/json.ts index 12651380219..e0cf131ad89 100644 --- a/src/vs/base/common/json.ts +++ b/src/vs/base/common/json.ts @@ -205,10 +205,10 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON token: SyntaxKind = SyntaxKind.Unknown, scanError: ScanError = ScanError.None; - function scanHexDigits(count: number, exact?: boolean): number { + function scanHexDigits(count: number): number { let digits = 0; let value = 0; - while (digits < count || !exact) { + while (digits < count) { let ch = text.charCodeAt(pos); if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) { value = value * 16 + ch - CharacterCodes._0; @@ -331,7 +331,7 @@ export function createScanner(text: string, ignoreTrivia: boolean = false): JSON result += '\t'; break; case CharacterCodes.u: - let ch = scanHexDigits(4, true); + let ch = scanHexDigits(4); if (ch >= 0) { result += String.fromCharCode(ch); } else { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 5b5bb7be830..09cc9f0fab4 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -551,9 +551,6 @@ export class WindowsManager implements IWindowsMainService { if (lastActiveWindow) { usedWindows.push(this.doAddFoldersToExistingWindow(lastActiveWindow, foldersToAdd)); } - - // Reset because we handled them - foldersToAdd = []; } // Handle files to open/diff or to create when we dont open a folder and we do not restore any folder/untitled from hot-exit diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 59ee56d0edd..f884af683dd 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -322,7 +322,10 @@ function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRu let pListParser: Promise<{ parse(content: string) }>; function getPListParser() { - return pListParser || import('fast-plist'); + if (!pListParser) { + pListParser = import('fast-plist'); + } + return pListParser; } function _loadSyntaxTokens(fileService: IFileService, themeLocation: URI, resultRules: ITokenColorizationRule[], resultColors: IColorMap): Promise { diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 2a0ab5b757d..219db8aa5c8 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -125,7 +125,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeData = ColorThemeData.fromStorageData(persistedThemeData); } let containerBaseTheme = this.getBaseThemeFromContainer(); - if (!themeData || themeData && themeData.baseTheme !== containerBaseTheme) { + if (!themeData || themeData.baseTheme !== containerBaseTheme) { themeData = ColorThemeData.createUnloadedTheme(containerBaseTheme); } themeData.setCustomColors(this.colorCustomizations); From 37e74c3135364bcbb8e5769aa1d9bef8c4179fd4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 17 Jan 2019 11:54:47 -0800 Subject: [PATCH 0365/1066] Use darken() function for listActiveSelectionBackground --- src/vs/platform/theme/common/styler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index dc424253896..3a2acb98e41 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, darken, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -190,7 +190,7 @@ export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeSer inputValidationErrorForeground: (style && style.inputValidationErrorForeground) || inputValidationErrorForeground, listFocusBackground: (style && style.listFocusBackground) || listFocusBackground, listFocusForeground: (style && style.listFocusForeground) || listFocusForeground, - listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || listActiveSelectionBackground, + listActiveSelectionBackground: (style && style.listActiveSelectionBackground) || darken(listActiveSelectionBackground, 0.1), listActiveSelectionForeground: (style && style.listActiveSelectionForeground) || listActiveSelectionForeground, listFocusAndSelectionBackground: style && style.listFocusAndSelectionBackground || listActiveSelectionBackground, listFocusAndSelectionForeground: (style && style.listFocusAndSelectionForeground) || listActiveSelectionForeground, @@ -232,7 +232,7 @@ export function attachListStyler(widget: IThemable, themeService: IThemeService, export const defaultListStyles: IColorMapping = { listFocusBackground: listFocusBackground, listFocusForeground: listFocusForeground, - listActiveSelectionBackground: listActiveSelectionBackground, + listActiveSelectionBackground: darken(listActiveSelectionBackground, 0.1), listActiveSelectionForeground: listActiveSelectionForeground, listFocusAndSelectionBackground: listActiveSelectionBackground, listFocusAndSelectionForeground: listActiveSelectionForeground, From 59d20555020fbbb7d2b71b48d57e9d03cf6a2aae Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Jan 2019 12:00:26 -0800 Subject: [PATCH 0366/1066] use createCachedData when possible --- src/main.js | 3 ++- src/vs/loader.js | 55 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/main.js b/src/main.js index 7429fcc087a..0b6c8e18249 100644 --- a/src/main.js +++ b/src/main.js @@ -268,7 +268,8 @@ function getNodeCachedDir() { } jsFlags() { - return this.value ? '--nolazy' : undefined; + // return this.value ? '--nolazy' : undefined; + return undefined; } ensureExists() { diff --git a/src/vs/loader.js b/src/vs/loader.js index 50f91caa43e..3c7c5d06d13 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -179,7 +179,7 @@ var AMDLoader; }; Utilities.forEachProperty = function (obj, callback) { if (obj) { - var key = undefined; + var key = void 0; for (key in obj) { if (obj.hasOwnProperty(key)) { callback(key, obj[key]); @@ -327,8 +327,8 @@ var AMDLoader; return options; }; ConfigurationOptionsUtil.mergeConfigurationOptions = function (overwrite, base) { - if (overwrite === undefined) { overwrite = null; } - if (base === undefined) { base = null; } + if (overwrite === void 0) { overwrite = null; } + if (base === void 0) { base = null; } var result = AMDLoader.Utilities.recursiveClone(base || {}); // Merge known properties and overwrite the unknown ones AMDLoader.Utilities.forEachProperty(overwrite, function (key, value) { @@ -660,6 +660,7 @@ var AMDLoader; this._env = env; this._didInitialize = false; this._didPatchNodeRequire = false; + this._hasCreateCachedData = false; } NodeScriptLoader.prototype._init = function (nodeRequire) { if (this._didInitialize) { @@ -671,6 +672,8 @@ var AMDLoader; this._vm = nodeRequire('vm'); this._path = nodeRequire('path'); this._crypto = nodeRequire('crypto'); + // check for `createCachedData`-api + this._hasCreateCachedData = typeof (new this._vm.Script('').createCachedData) === 'function'; }; // patch require-function of nodejs such that we can manually create a script // from cached data. this is done by overriding the `Module._compile` function @@ -711,7 +714,7 @@ var AMDLoader; options.cachedData = that._fs.readFileSync(cachedDataPath); } catch (e) { - options.produceCachedData = true; + options.produceCachedData = !that._hasCreateCachedData; } var script = new that._vm.Script(wrapper, options); var compileWrapper = script.runInThisContext(options); @@ -719,7 +722,7 @@ var AMDLoader; var require = makeRequireFunction(this); var args = [this.exports, require, this, filename, dirname, process, _commonjsGlobal, Buffer]; var result = compileWrapper.apply(this.exports, args); - that._processCachedData(moduleManager, script, cachedDataPath); + that._processCachedData(moduleManager, script, wrapper, cachedDataPath, !options.cachedData); return result; }; }; @@ -778,15 +781,15 @@ var AMDLoader; } else { var cachedDataPath_1 = _this._getCachedDataPath(opts.nodeCachedData.seed, opts.nodeCachedData.path, scriptSrc); - _this._fs.readFile(cachedDataPath_1, function (err, cachedData) { + _this._fs.readFile(cachedDataPath_1, function (_err, cachedData) { // create script options var options = { filename: vmScriptSrc, - produceCachedData: typeof cachedData === 'undefined', + produceCachedData: !_this._hasCreateCachedData && typeof cachedData === 'undefined', cachedData: cachedData }; var script = _this._loadAndEvalScript(moduleManager, scriptSrc, vmScriptSrc, contents, options, recorder, callback, errorback); - _this._processCachedData(moduleManager, script, cachedDataPath_1); + _this._processCachedData(moduleManager, script, contents, cachedDataPath_1, !options.cachedData); }); } }); @@ -820,7 +823,7 @@ var AMDLoader; var basename = this._path.basename(filename).replace(/\.js$/, ''); return this._path.join(basedir, basename + "-" + hash + ".code"); }; - NodeScriptLoader.prototype._processCachedData = function (moduleManager, script, cachedDataPath) { + NodeScriptLoader.prototype._processCachedData = function (moduleManager, script, contents, cachedDataPath, createCachedData) { var _this = this; if (script.cachedDataRejected) { // data rejected => delete cache file @@ -838,13 +841,12 @@ var AMDLoader; }); } }); - }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay); + }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay / 2); } else if (script.cachedDataProduced) { // data produced => tell outside world moduleManager.getConfig().getOptionsLiteral().nodeCachedData.onData(undefined, { - path: cachedDataPath, - length: script.cachedData.length + path: cachedDataPath }); // data produced => write cache file NodeScriptLoader._runSoon(function () { @@ -859,6 +861,27 @@ var AMDLoader; }); }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay); } + else if (this._hasCreateCachedData && createCachedData) { + // NEW world + // data produced => tell outside world + moduleManager.getConfig().getOptionsLiteral().nodeCachedData.onData(undefined, { + path: cachedDataPath + }); + // soon'ish create and save cached data + NodeScriptLoader._runSoon(function () { + var data = script.createCachedData(contents); + _this._fs.writeFile(cachedDataPath, data, function (err) { + if (!err) { + return; + } + moduleManager.getConfig().getOptionsLiteral().nodeCachedData.onData({ + errorCode: 'writeFile', + path: cachedDataPath, + detail: err + }); + }); + }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay * 2); + } }; NodeScriptLoader._runSoon = function (callback, minTimeout) { var timeout = minTimeout + Math.ceil(Math.random() * minTimeout); @@ -1067,7 +1090,7 @@ var AMDLoader; AMDLoader.PluginDependency = PluginDependency; var ModuleManager = (function () { function ModuleManager(env, scriptLoader, defineFunc, requireFunc, loaderAvailableTimestamp) { - if (loaderAvailableTimestamp === undefined) { loaderAvailableTimestamp = 0; } + if (loaderAvailableTimestamp === void 0) { loaderAvailableTimestamp = 0; } this._env = env; this._scriptLoader = scriptLoader; this._loaderAvailableTimestamp = loaderAvailableTimestamp; @@ -1186,7 +1209,7 @@ var AMDLoader; */ ModuleManager.prototype.defineModule = function (strModuleId, dependencies, callback, errorback, stack, moduleIdResolver) { var _this = this; - if (moduleIdResolver === undefined) { moduleIdResolver = new ModuleIdResolver(strModuleId); } + if (moduleIdResolver === void 0) { moduleIdResolver = new ModuleIdResolver(strModuleId); } var moduleId = this._moduleIdProvider.getModuleId(strModuleId); if (this._modules2[moduleId]) { if (!this._config.isDuplicateMessageIgnoredFor(strModuleId)) { @@ -1245,7 +1268,7 @@ var AMDLoader; * @return The exports of module 'id' */ ModuleManager.prototype.synchronousRequire = function (_strModuleId, moduleIdResolver) { - if (moduleIdResolver === undefined) { moduleIdResolver = new ModuleIdResolver(_strModuleId); } + if (moduleIdResolver === void 0) { moduleIdResolver = new ModuleIdResolver(_strModuleId); } var dependency = this._normalizeDependency(_strModuleId, moduleIdResolver); var m = this._modules2[dependency.id]; if (!m) { @@ -1637,7 +1660,7 @@ var AMDLoader; jQuery: true }; var _requireFunc_config = function (params, shouldOverwrite) { - if (shouldOverwrite === undefined) { shouldOverwrite = false; } + if (shouldOverwrite === void 0) { shouldOverwrite = false; } moduleManager.configure(params, shouldOverwrite); }; var RequireFunc = function () { From e0a3f26e072ff3331e4acd249dd5282164d4f293 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 17 Jan 2019 13:18:22 -0800 Subject: [PATCH 0367/1066] Emmet dependency for #66013 --- extensions/emmet/package.json | 5 +++-- extensions/emmet/yarn.lock | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 537911525bc..9805a694c83 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -435,7 +435,8 @@ }, "scripts": { "watch": "gulp watch-extension:emmet", - "compile": "gulp compile-extension:emmet" + "compile": "gulp compile-extension:emmet", + "deps": "yarn add vscode-emmet-helper" }, "devDependencies": { "@types/node": "8.0.33", @@ -448,6 +449,6 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^0.1.1", "image-size": "^0.5.2", - "vscode-emmet-helper": "^1.2.14" + "vscode-emmet-helper": "^1.2.15" } } diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 8c10f2a7567..1fb4a9aff41 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -2469,10 +2469,10 @@ vinyl@~2.0.1: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -vscode-emmet-helper@^1.2.14: - version "1.2.14" - resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.14.tgz#11b97b86dbebd952a77cb278fdf921f59a36de2a" - integrity sha512-hhiWVK+FzsbSqfk07h8UYk73n2SOtxpdbGal7v+jUSsD2IbHNM2NeqEgfRY0GUtVnobufM4xbPNgaoR+MOc7pw== +vscode-emmet-helper@^1.2.15: + version "1.2.15" + resolved "https://registry.yarnpkg.com/vscode-emmet-helper/-/vscode-emmet-helper-1.2.15.tgz#62dbfbf49bb9ebe329cb7bffdda5aaac725eea7a" + integrity sha512-JplvmMMWSvm/6/dZezix2ADPM49u6YahPYjs/QToohUpomW/2Eb27ecCrkCyOGBPfKLKGiOPHCssss8TSDA9ag== dependencies: "@emmetio/extract-abbreviation" "0.1.6" jsonc-parser "^1.0.0" From d3514624bd9ebf8b9ad1f6f2b3e526a8ad495c3e Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Thu, 17 Jan 2019 22:25:40 +0100 Subject: [PATCH 0368/1066] properly use paths --- .../parts/debug/electron-browser/rawDebugSession.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts index 0409ac76066..8801d1105ec 100644 --- a/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/rawDebugSession.ts @@ -9,7 +9,7 @@ import * as objects from 'vs/base/common/objects'; import { Action } from 'vs/base/common/actions'; import * as errors from 'vs/base/common/errors'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { formatPII } from 'vs/workbench/parts/debug/common/debugUtils'; +import { formatPII, isUri } from 'vs/workbench/parts/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/parts/debug/common/debug'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import * as cp from 'child_process'; @@ -558,7 +558,12 @@ export class RawDebugSession { spawnOpts.env = envArgs; } - let spawnArgs = vscodeArgs.args.map(a => (a.prefix || '') + (a.path || '')); + let spawnArgs = vscodeArgs.args.map(a => { + if ((a.prefix === '--file-uri=' || a.prefix === '--folder-uri=') && !isUri(a.path)) { + return a.path; + } + return (a.prefix || '') + (a.path || ''); + }); let runtimeExecutable = this.environmentService['execPath']; if (!runtimeExecutable) { From 260040ec0ad6df01da9494dbe462b574af062931 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 17 Jan 2019 21:43:27 +0000 Subject: [PATCH 0369/1066] Bump node2 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 432ad403ea5..b730189c0f1 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -16,7 +16,7 @@ }, { "name": "ms-vscode.node-debug2", - "version": "1.31.2", + "version": "1.31.3", "repo": "https://github.com/Microsoft/vscode-node-debug2", "metadata": { "id": "36d19e17-7569-4841-a001-947eb18602b2", From ee3a6166018471de7cd7ab679c441fdafde3a942 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 17 Jan 2019 17:11:28 -0800 Subject: [PATCH 0370/1066] Max code actions per file is 1000 --- src/vs/workbench/api/node/extHostLanguageFeatures.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index df6512c9b6b..e12de7857c0 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -282,6 +282,7 @@ export interface CustomCodeAction extends CodeActionDto { } class CodeActionAdapter { + private static readonly _maxCodeActionsPerFile: number = 1000; constructor( private readonly _documents: ExtHostDocuments, @@ -302,7 +303,10 @@ class CodeActionAdapter { for (const diagnostic of this._diagnostics.getDiagnostics(resource)) { if (ran.intersection(diagnostic.range)) { - allDiagnostics.push(diagnostic); + const newLen = allDiagnostics.push(diagnostic); + if (newLen > CodeActionAdapter._maxCodeActionsPerFile) { + break; + } } } From 91298fd627fb06b476dbff551ae169c1e180549a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 17 Jan 2019 17:43:31 -0800 Subject: [PATCH 0371/1066] tweak timings --- src/vs/loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/loader.js b/src/vs/loader.js index 3c7c5d06d13..4eddcab3a02 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -880,7 +880,7 @@ var AMDLoader; detail: err }); }); - }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay * 2); + }, moduleManager.getConfig().getOptionsLiteral().nodeCachedData.writeDelay); } }; NodeScriptLoader._runSoon = function (callback, minTimeout) { From d99486b634200ea92505ac7956d6c98f7d25384b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 17 Jan 2019 17:02:26 +0100 Subject: [PATCH 0372/1066] Fix diff editor check --- src/vs/editor/browser/widget/diffEditorWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index a726bdd0491..1f32835046f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -743,7 +743,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE } public restoreViewState(s: editorCommon.IDiffEditorViewState): void { - if (s.original && s.original) { + if (s.original && s.modified) { let diffEditorState = s; this.originalEditor.restoreViewState(diffEditorState.original); this.modifiedEditor.restoreViewState(diffEditorState.modified); From 24b9056eb163314a43d73be14ea979b67ae51e57 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Jan 2019 08:31:44 +0100 Subject: [PATCH 0373/1066] Fix lgtm issues --- .../currentLineHighlight/currentLineHighlight.ts | 1 - .../currentLineMarginHighlight.ts | 1 - src/vs/editor/common/config/editorOptions.ts | 4 ++-- .../editor/common/controller/cursorTypeOperations.ts | 2 +- src/vs/editor/common/model.ts | 6 ++++++ src/vs/editor/common/model/intervalTree.ts | 2 -- src/vs/editor/common/model/textModel.ts | 10 ++++++++++ src/vs/editor/common/model/textModelTokens.ts | 2 +- src/vs/editor/common/model/wordHelper.ts | 4 ---- .../common/modes/languageConfigurationRegistry.ts | 2 +- src/vs/editor/common/viewModel/viewModelDecorations.ts | 3 +-- .../editor/standalone/common/monarch/monarchCompile.ts | 2 -- src/vs/platform/integrity/node/integrityServiceImpl.ts | 2 +- .../keybinding/common/macLinuxKeyboardMapper.ts | 2 +- 14 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 20ea1f461d3..5a49e8d3070 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -65,7 +65,6 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay { const selectionIsEmpty = e.selections[0].isEmpty(); if (this._selectionIsEmpty !== selectionIsEmpty) { this._selectionIsEmpty = selectionIsEmpty; - hasChanged = true; return true; } diff --git a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts index 31781146edb..b3054d0408a 100644 --- a/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineMarginHighlight/currentLineMarginHighlight.ts @@ -63,7 +63,6 @@ export class CurrentLineMarginHighlightOverlay extends DynamicViewOverlay { const selectionIsEmpty = e.selections[0].isEmpty(); if (this._selectionIsEmpty !== selectionIsEmpty) { this._selectionIsEmpty = selectionIsEmpty; - hasChanged = true; return true; } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 57173f08a01..d8ee1d9226b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1954,7 +1954,7 @@ export class EditorOptionsValidator { } else if (renderWhitespace === false) { renderWhitespace = 'none'; } - renderWhitespace = _stringSet<'none' | 'boundary' | 'all'>(opts.renderWhitespace, defaults.renderWhitespace, ['none', 'boundary', 'all']); + renderWhitespace = _stringSet<'none' | 'boundary' | 'all'>(renderWhitespace, defaults.renderWhitespace, ['none', 'boundary', 'all']); } let renderLineHighlight = opts.renderLineHighlight; @@ -1965,7 +1965,7 @@ export class EditorOptionsValidator { } else if (renderLineHighlight === false) { renderLineHighlight = 'none'; } - renderLineHighlight = _stringSet<'none' | 'gutter' | 'line' | 'all'>(opts.renderLineHighlight, defaults.renderLineHighlight, ['none', 'gutter', 'line', 'all']); + renderLineHighlight = _stringSet<'none' | 'gutter' | 'line' | 'all'>(renderLineHighlight, defaults.renderLineHighlight, ['none', 'gutter', 'line', 'all']); } let mouseWheelScrollSensitivity = _float(opts.mouseWheelScrollSensitivity, defaults.scrollbar.mouseWheelScrollSensitivity); diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 2097cb3817d..ac585844d86 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -159,7 +159,7 @@ export class TypeOperations { action = expectedIndentAction.action; indentation = expectedIndentAction.indentation; } else if (lineNumber > 1) { - let lastLineNumber = lineNumber - 1; + let lastLineNumber: number; for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) { let lineText = model.getLineContent(lastLineNumber); let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineText); diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 9912616baad..9d9a1a7d8c4 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -732,6 +732,12 @@ export interface ITextModel { */ findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; + /** + * Flush all tokenization state. + * @internal + */ + flushTokens(): void; + /** * Force tokenization information for `lineNumber` to be accurate. * @internal diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index 1800edbf0e4..496e77b9ef4 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -464,11 +464,9 @@ export function nodeAcceptEdit(node: IntervalNode, start: number, end: number, t const deltaColumn = (insertingCnt - deletingCnt); if (!startDone) { node.start = Math.max(0, nodeStart + deltaColumn); - startDone = true; } if (!endDone) { node.end = Math.max(0, nodeEnd + deltaColumn); - endDone = true; } if (node.start > node.end) { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 449a69f35c0..eb07a0cd49f 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1840,6 +1840,16 @@ export class TextModel extends Disposable implements model.ITextModel { } } + public flushTokens(): void { + this._resetTokenizationState(); + this.emitModelTokensChangedEvent({ + ranges: [{ + fromLineNumber: 1, + toLineNumber: this.getLineCount() + }] + }); + } + public forceTokenization(lineNumber: number): void { if (lineNumber < 1 || lineNumber > this.getLineCount()) { throw new Error('Illegal value for lineNumber'); diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index c5f2e74f034..02b3a946099 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -154,7 +154,7 @@ class ModelLineTokens { let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex); if (fromTokenIndex > 0) { - const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0); + const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1]; if (fromTokenStartOffset === chIndex) { fromTokenIndex--; } diff --git a/src/vs/editor/common/model/wordHelper.ts b/src/vs/editor/common/model/wordHelper.ts index b39bc6a563d..3e849b0dddf 100644 --- a/src/vs/editor/common/model/wordHelper.ts +++ b/src/vs/editor/common/model/wordHelper.ts @@ -60,10 +60,6 @@ function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, let pos = column - 1 - textOffset; let start = text.lastIndexOf(' ', pos - 1) + 1; - let end = text.indexOf(' ', pos); - if (end === -1) { - end = text.length; - } wordDefinition.lastIndex = start; let match: RegExpMatchArray | null; diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 0efcb9559f4..1c46797d5e7 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -332,7 +332,7 @@ export class LanguageConfigurationRegistryImpl { private getPrecedingValidLine(model: IVirtualModel, lineNumber: number, indentRulesSupport: IndentRulesSupport) { let languageID = model.getLanguageIdAtPosition(lineNumber, 0); if (lineNumber > 1) { - let lastLineNumber = lineNumber - 1; + let lastLineNumber: number; let resultLineNumber = -1; for (lastLineNumber = lineNumber - 1; lastLineNumber >= 1; lastLineNumber--) { diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index c8020eb4823..96944088919 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -92,8 +92,7 @@ export class ViewModelDecorations implements IDisposable { } public getDecorationsViewportData(viewRange: Range): IDecorationsViewportData { - let cacheIsValid = true; - cacheIsValid = cacheIsValid && (this._cachedModelDecorationsResolver !== null); + let cacheIsValid = (this._cachedModelDecorationsResolver !== null); cacheIsValid = cacheIsValid && (viewRange.equalsRange(this._cachedModelDecorationsResolverViewRange)); if (!cacheIsValid) { this._cachedModelDecorationsResolver = this._getDecorationsViewportData(viewRange); diff --git a/src/vs/editor/standalone/common/monarch/monarchCompile.ts b/src/vs/editor/standalone/common/monarch/monarchCompile.ts index 93332bf9432..6609690f55a 100644 --- a/src/vs/editor/standalone/common/monarch/monarchCompile.ts +++ b/src/vs/editor/standalone/common/monarch/monarchCompile.ts @@ -219,7 +219,6 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: else if (action.token || action.token === '') { if (typeof (action.token) !== 'string') { throw monarchCommon.createError(lexer, 'a \'token\' attribute must be of type string, in rule: ' + ruleName); - return { token: '' }; } else { // only copy specific typed fields (only happens once during compile Lexer) @@ -321,7 +320,6 @@ function compileAction(lexer: monarchCommon.ILexerMin, ruleName: string, action: } else { throw monarchCommon.createError(lexer, 'an action must be a string, an object with a \'token\' or \'cases\' attribute, or an array of actions; in rule: ' + ruleName); - return ''; } } diff --git a/src/vs/platform/integrity/node/integrityServiceImpl.ts b/src/vs/platform/integrity/node/integrityServiceImpl.ts index 64ee7abfc42..25970d2f57f 100644 --- a/src/vs/platform/integrity/node/integrityServiceImpl.ts +++ b/src/vs/platform/integrity/node/integrityServiceImpl.ts @@ -115,7 +115,7 @@ export class IntegrityServiceImpl implements IIntegrityService { return Promise.all(asyncResults).then((allResults) => { let isPure = true; - for (let i = 0, len = allResults.length; isPure && i < len; i++) { + for (let i = 0, len = allResults.length; i < len; i++) { if (!allResults[i].isPure) { isPure = false; break; diff --git a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts index 020b8f1890b..515a166f134 100644 --- a/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts +++ b/src/vs/workbench/services/keybinding/common/macLinuxKeyboardMapper.ts @@ -800,7 +800,7 @@ export class MacLinuxKeyboardMapper implements IKeyboardMapper { for (let i = 0, len = kbCombos.length; i < len; i++) { const kbCombo = kbCombos[i]; // find out the priority of this scan code for this key code - let colPriority = '-'; + let colPriority: string; const scanCodeCombos = this._scanCodeKeyCodeMapper.lookupKeyCodeCombo(kbCombo); if (scanCodeCombos.length === 1) { From 258c55e7444dd545c3722a91769dba71f2cbbb8f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Jan 2019 09:47:00 +0100 Subject: [PATCH 0374/1066] list aria-level, tree uses depth related to #66584 --- src/vs/base/browser/ui/list/listWidget.ts | 19 +++++++++++++++---- src/vs/base/browser/ui/tree/abstractTree.ts | 3 +++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 2f0c0312532..e1272b58b39 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -627,9 +627,14 @@ export interface IAccessibilityProvider { * Returning null will not disable ARIA for the element. Instead it is up to the screen reader * to compute a meaningful label based on the contents of the element in the DOM * - * See also: https://www.w3.org/TR/wai-aria/states_and_properties#aria-label + * See also: https://www.w3.org/TR/wai-aria/#aria-label */ getAriaLabel(element: T): string | null; + + /** + * https://www.w3.org/TR/wai-aria/#aria-level + */ + getAriaLevel?(element: T): number | undefined; } export class DefaultStyleController implements IStyleController { @@ -918,9 +923,7 @@ class AccessibiltyRenderer implements IListRenderer { templateId: string = 'a18n'; - constructor(private accessibilityProvider: IAccessibilityProvider) { - - } + constructor(private accessibilityProvider: IAccessibilityProvider) { } renderTemplate(container: HTMLElement): HTMLElement { return container; @@ -934,6 +937,14 @@ class AccessibiltyRenderer implements IListRenderer { } else { container.removeAttribute('aria-label'); } + + const ariaLevel = this.accessibilityProvider.getAriaLevel && this.accessibilityProvider.getAriaLevel(element); + + if (typeof ariaLevel === 'number') { + container.setAttribute('aria-level', `${ariaLevel}`); + } else { + container.removeAttribute('aria-level'); + } } disposeTemplate(templateData: any): void { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e8e7cdeeea4..8f5c18c5022 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -131,6 +131,9 @@ function asListOptions(modelProvider: () => ITreeModel Date: Fri, 18 Jan 2019 10:23:36 +0100 Subject: [PATCH 0375/1066] async data tree: stop promise spawning --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 405c4161d54..e0a564f7fdb 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -558,7 +558,7 @@ export class AsyncDataTree implements IDisposable let result: Promise | undefined; this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { - if (intersects(refreshNode, node)) { + if (!result && intersects(refreshNode, node)) { result = refreshPromise.then(() => this.queueRefresh(node, recursive, reason, viewStateContext)); } }); From 50c858a287f28128e5dd912ba43f6a7c98b9a31c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Jan 2019 11:21:26 +0100 Subject: [PATCH 0376/1066] perf - reduce work on file events in config --- .../services/configuration/node/configuration.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index db77e6eba2b..338a3ddc1f7 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -306,7 +306,6 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura // Find changes that affect workspace configuration files for (let i = 0, len = events.length; i < len; i++) { - const resource = events[i].resource; const basename = resources.basename(resource); const isJson = paths.extname(basename) === '.json'; @@ -324,6 +323,7 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura // Handle case where ".vscode" got deleted if (isDeletedSettingsFolder) { affectedByChanges = true; + break; } // only valid workspace config files @@ -331,12 +331,8 @@ export class FileServiceBasedFolderConfiguration extends AbstractFolderConfigura continue; } - switch (events[i].type) { - case FileChangeType.DELETED: - case FileChangeType.UPDATED: - case FileChangeType.ADDED: - affectedByChanges = true; - } + affectedByChanges = true; + break; } if (affectedByChanges) { From c037ce76d1653a977f367b4cd767937121d5a7a4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 11:27:34 +0100 Subject: [PATCH 0377/1066] #66364 select also when focused in search box --- .../preferences/electron-browser/preferences.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index 339bf73f6d6..8b06f6018e1 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -262,7 +262,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: KEYBINDINGS_EDITOR_COMMAND_SEARCH, weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS.toNegated()), + when: ContextKeyExpr.and(CONTEXT_KEYBINDINGS_EDITOR), primary: KeyMod.CtrlCmd | KeyCode.KEY_F, handler: (accessor, args: any) => { const control = accessor.get(IEditorService).activeControl as IKeybindingsEditor; From f85db57639a1de9416481e1cc07dc80a98b5e07e Mon Sep 17 00:00:00 2001 From: SimonEggert <33206628+SimonEggert@users.noreply.github.com> Date: Fri, 18 Jan 2019 11:54:26 +0100 Subject: [PATCH 0378/1066] Add setting for tab closing order (#66635) * Add tabClosingOrder, set default mru and use setting to focus next tab * Change domAction name * Fix formatting * Use mru behavior if no tabClosingOrder is specified * Add tests for ltr and rtl tabClosingOrder * Create group with changed settings to fix tests * Change setting to boolean, remove unneeded code changes, avoid multiple method-calls, only calculate setting when config changes, adjust tests --- .../workbench/browser/parts/editor/editor.ts | 1 + src/vs/workbench/common/editor.ts | 1 + src/vs/workbench/common/editor/editorGroup.ts | 18 +++++- .../electron-browser/main.contribution.ts | 5 ++ .../test/common/editor/editorGroups.test.ts | 60 ++++++++++++++++++- 5 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 2c460e95757..09b13c06a1b 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -30,6 +30,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { highlightModifiedTabs: false, tabCloseButton: 'right', tabSizing: 'fit', + closeTabsInMRUOrder: true, showIcons: true, enablePreview: true, openPositioning: 'right', diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index b628d19584b..ae666377dd3 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -941,6 +941,7 @@ export interface IWorkbenchEditorPartConfiguration { highlightModifiedTabs?: boolean; tabCloseButton?: 'left' | 'right' | 'off'; tabSizing?: 'fit' | 'shrink'; + closeTabsInMRUOrder?: boolean; showIcons?: boolean; enablePreview?: boolean; enablePreviewFromQuickOpen?: boolean; diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 650785bc720..00a275cd8fb 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -98,6 +98,7 @@ export class EditorGroup extends Disposable { private active: EditorInput | null; // editor in active state private editorOpenPositioning: 'left' | 'right' | 'first' | 'last'; + private closeTabsInMRUOrder: boolean; constructor( labelOrSerializedGroup: ISerializedEditorGroup, @@ -122,6 +123,7 @@ export class EditorGroup extends Disposable { private onConfigurationUpdated(event?: IConfigurationChangeEvent): void { this.editorOpenPositioning = this.configurationService.getValue('workbench.editor.openPositioning'); + this.closeTabsInMRUOrder = this.configurationService.getValue('workbench.editor.closeTabsInMRUOrder'); } get id(): GroupIdentifier { @@ -332,7 +334,21 @@ export class EditorGroup extends Disposable { // More than one editor if (this.mru.length > 1) { - this.setActive(this.mru[1]); // active editor is always first in MRU, so pick second editor after as new active + + let newActive: EditorInput; + + if (this.closeTabsInMRUOrder) { + newActive = this.mru[1]; // active editor is always first in MRU, so pick second editor after as new active + } + else { + if (index === this.editors.length - 1) { + newActive = this.editors[index - 1]; // last editor is closed, pick previous as new active + } + else { + newActive = this.editors[index + 1]; // pick next editor as new active + } + } + this.setActive(newActive); } // One Editor diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index e0a5e825a18..d74e94e7923 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -516,6 +516,11 @@ configurationRegistry.registerConfiguration({ ], '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."), + '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."), diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index b173955c393..c69335c58be 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -29,7 +29,7 @@ function inst(): IInstantiationService { inst.stub(ITelemetryService, NullTelemetryService); const config = new TestConfigurationService(); - config.setUserConfiguration('workbench', { editor: { openPositioning: 'right' } }); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right', closeTabsInMRUOrder: true } }); inst.stub(IConfigurationService, config); return inst; @@ -645,6 +645,64 @@ suite('Workbench editor groups', () => { assert.equal(group.count, 0); }); + test('Multiple Editors - closing picks next to the right', function () { + let inst = new TestInstantiationService(); + inst.stub(IStorageService, new TestStorageService()); + inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IWorkspaceContextService, new TestContextService()); + inst.stub(ITelemetryService, NullTelemetryService); + + const config = new TestConfigurationService(); + config.setUserConfiguration('workbench', { editor: { closeTabsInMRUOrder: false } }); + inst.stub(IConfigurationService, config); + + const group = inst.createInstance(EditorGroup); + const events = groupListener(group); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + const input4 = input(); + const input5 = input(); + + group.openEditor(input1, { pinned: true, active: true }); + group.openEditor(input2, { pinned: true, active: true }); + group.openEditor(input3, { pinned: true, active: true }); + group.openEditor(input4, { pinned: true, active: true }); + group.openEditor(input5, { pinned: true, active: true }); + + assert.equal(group.activeEditor, input5); + assert.equal(group.getEditors(true)[0], input5); + assert.equal(group.count, 5); + + group.closeEditor(input5); + assert.equal(group.activeEditor, input4); + assert.equal(events.activated[5], input4); + assert.equal(group.count, 4); + + group.setActive(input1); + group.closeEditor(input1); + + assert.equal(group.activeEditor, input2); + assert.equal(group.count, 3); + + group.setActive(input3); + group.closeEditor(input3); + + assert.equal(group.activeEditor, input4); + assert.equal(group.count, 2); + + group.closeEditor(input4); + + assert.equal(group.activeEditor, input2); + assert.equal(group.count, 1); + + group.closeEditor(input2); + + assert.ok(!group.activeEditor); + assert.equal(group.count, 0); + }); + test('Multiple Editors - move editor', function () { const group = createGroup(); const events = groupListener(group); From c4b89c1be68a6aa23d7bed0031e4705fd4a77e8c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Jan 2019 11:58:38 +0100 Subject: [PATCH 0379/1066] :lipstick: --- src/vs/workbench/common/editor/editorGroup.ts | 9 +++------ src/vs/workbench/electron-browser/main.contribution.ts | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 00a275cd8fb..f8d1e48fe61 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -334,20 +334,17 @@ export class EditorGroup extends Disposable { // More than one editor if (this.mru.length > 1) { - let newActive: EditorInput; - if (this.closeTabsInMRUOrder) { newActive = this.mru[1]; // active editor is always first in MRU, so pick second editor after as new active - } - else { + } else { if (index === this.editors.length - 1) { newActive = this.editors[index - 1]; // last editor is closed, pick previous as new active - } - else { + } else { newActive = this.editors[index + 1]; // pick next editor as new active } } + this.setActive(newActive); } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index d74e94e7923..f478a2cc03a 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -518,7 +518,7 @@ configurationRegistry.registerConfiguration({ }, 'workbench.editor.closeTabsInMRUOrder': { 'type': 'boolean', - 'description': nls.localize('closeTabsInMRUOrder', "Controls whether tabs are closed in most recently used order."), + 'description': nls.localize('closeTabsInMRUOrder', "Controls whether tabs are closed in most recently used order or from left to right."), 'default': true }, 'workbench.editor.showIcons': { From 9ed0cc38b7a8ba2acecb354de030e4126d9f6109 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Jan 2019 12:47:06 +0100 Subject: [PATCH 0380/1066] place type filter outside tree --- src/vs/base/browser/ui/list/list.css | 18 ++++++++----- src/vs/base/browser/ui/list/listWidget.ts | 2 +- src/vs/base/browser/ui/tree/abstractTree.ts | 27 ++++++++++++++----- src/vs/base/common/color.ts | 16 +++++++++++ src/vs/platform/theme/common/colorRegistry.ts | 15 +++++++++++ src/vs/platform/theme/common/styler.ts | 4 +-- 6 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 61bd0dbf52c..2c5841a67c0 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -64,10 +64,15 @@ } /* Type filter */ +@keyframes monaco-list-type-filter-fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + .monaco-list-type-filter { position: absolute; - right: 5px; - top: 4px; + /* right: 2px; + top: 1px; */ border-radius: 2px; border-width: 1px; border-style: solid; @@ -77,8 +82,9 @@ overflow: hidden; text-align: right; box-sizing: border-box; -} - -.monaco-list-type-filter:empty { - display: none; + animation: monaco-list-type-filter-fade-in 150ms linear 0s; + font-size: 13px; + line-height: 18px; + height: 20px; + z-index: 100; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 931f6a186b1..21118a86412 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -730,7 +730,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listMatchesOutline) { - content.push(`.monaco-list-type-filter { border-color: ${styles.listMatchesOutline} }`); + content.push(`.monaco-list-type-filter { border-color: ${styles.listMatchesOutline}; }`); } const newStyles = content.join('\n'); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b845937015b..545cd8d1f0d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, timeout } from 'vs/base/browser/dom'; +import { append, $, toggleClass, timeout, getDomNodePagePosition } from 'vs/base/browser/dom'; import { Event, Relay, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -309,13 +309,13 @@ class TypeFilter implements ITreeFilter { if (!score) { // DEMO: just highlights - // return { data: FuzzyScore.Default, visibility: true }; + return { data: FuzzyScore.Default, visibility: true }; // DEMO: filter // return TreeVisibility.Recurse; // DEMO: smarter filter - return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } return { data: score, visibility: true }; @@ -337,15 +337,14 @@ class TypeFilterController implements IDisposable { constructor( private tree: AbstractTree, - view: List>, + private view: List>, private filter: TypeFilter, keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider ) { - const container = view.getHTMLElement(); - this.domNode = append(container, $('.monaco-list-type-filter')); + this.domNode = $('.monaco-list-type-filter'); const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); - const onInput = Event.chain(domEvent(container, 'keydown')) + const onInput = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e)) @@ -367,6 +366,20 @@ class TypeFilterController implements IDisposable { } private onInput(pattern: string): void { + const container = this.view.getHTMLElement(); + + if (pattern && !this.domNode.parentElement) { + const { top, left } = getDomNodePagePosition(container); + + this.domNode.style.top = `${top - 22}px`; + this.domNode.style.left = `${left + 2}px`; + this.domNode.style.maxWidth = `${container.clientWidth}px`; + document.body.append(this.domNode); + + } else if (!pattern && this.domNode.parentElement) { + this.domNode.remove(); + } + this._pattern = pattern; this.domNode.textContent = pattern; this.filter.pattern = pattern; diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index f63e7aa86f1..fd8f70afd8d 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -399,6 +399,22 @@ export class Color { return new Color(new RGBA(r, g, b, a)); } + blend2(c: Color): Color { + const otherRgba = c.rgba; + + // Convert to 0..1 opacity + const thisA = this.rgba.a; + const otherA = otherRgba.a; + + const totalA = thisA + otherA; + + const r = this.rgba.r * thisA / totalA + otherRgba.r * otherA / totalA; + const g = this.rgba.g * thisA / totalA + otherRgba.g * otherA / totalA; + const b = this.rgba.b * thisA / totalA + otherRgba.b * otherA / totalA; + + return new Color(new RGBA(r, g, b, 1)); + } + flatten(...backgrounds: Color[]): Color { const background = backgrounds.reduceRight((accumulator, color) => { return Color._flatten(color, accumulator); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index d239891f730..61e2b539623 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -407,6 +407,21 @@ function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, }; } +export function blend2(transparentColorValue: ColorValue, opaqueColorValue: ColorValue): ColorFunction { + return (theme) => { + let transparentColor = resolveColorValue(transparentColorValue, theme); + let opaqueColor = resolveColorValue(opaqueColorValue, theme); + if (transparentColor && opaqueColor) { + return opaqueColor.blend2(transparentColor); + } else if (transparentColor) { + return transparentColor; + } else if (opaqueColor) { + return opaqueColor; + } + return null; + }; +} + // ----- implementation /** diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index b38c6ed59c9..1cba060e906 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, editorFindMatchHighlightBorder, darken } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, editorFindMatchHighlightBorder, darken, editorWidgetBackground, blend2 } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -247,7 +247,7 @@ export const defaultListStyles: IColorMapping = { listFocusOutline: activeContrastBorder, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, - listMatchesBackground: editorFindMatchHighlight, + listMatchesBackground: blend2(editorFindMatchHighlight, editorWidgetBackground), listMatchesOutline: editorFindMatchHighlightBorder }; From b1eb540216f7e90325e673acc1e587951f4d915e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 13:38:25 +0100 Subject: [PATCH 0381/1066] fix strict null errors --- src/vs/workbench/services/label/common/labelService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index f6b4481909a..978f39eb281 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -104,7 +104,7 @@ export class LabelService implements ILabelService { } findFormatting(resource: URI): ResourceLabelFormatting | undefined { - let bestResult: ResourceLabelFormatter; + let bestResult: ResourceLabelFormatter | undefined; this.formatters.forEach(formatter => { if (formatter.scheme === resource.scheme) { From 13affa09f87a9c1e8b1237c600267889128d5866 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Jan 2019 13:40:07 +0100 Subject: [PATCH 0382/1066] Add Force Tokenization command --- .../contrib/tokenization/tokenization.ts | 34 +++++++++++++++++++ src/vs/editor/editor.all.ts | 1 + 2 files changed, 35 insertions(+) create mode 100644 src/vs/editor/contrib/tokenization/tokenization.ts diff --git a/src/vs/editor/contrib/tokenization/tokenization.ts b/src/vs/editor/contrib/tokenization/tokenization.ts new file mode 100644 index 00000000000..aeb5b996e1a --- /dev/null +++ b/src/vs/editor/contrib/tokenization/tokenization.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; +import { StopWatch } from 'vs/base/common/stopwatch'; + +class ForceRetokenizeAction extends EditorAction { + constructor() { + super({ + id: 'editor.action.forceRetokenize', + label: nls.localize('forceRetokenize', "Developer: Force Retokenize"), + alias: 'Developer: Force Retokenize', + precondition: null + }); + } + + public run(accessor: ServicesAccessor, editor: ICodeEditor): void { + if (!editor.hasModel()) { + return; + } + const model = editor.getModel(); + model.flushTokens(); + const sw = new StopWatch(true); + model.forceTokenization(model.getLineCount()); + sw.stop(); + console.log(`tokenization took ${sw.elapsed()}`); + } +} + +registerEditorAction(ForceRetokenizeAction); diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index d4d7ef65ee7..4bca35be26d 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -36,6 +36,7 @@ import 'vs/editor/contrib/rename/rename'; import 'vs/editor/contrib/smartSelect/smartSelect'; import 'vs/editor/contrib/snippet/snippetController2'; import 'vs/editor/contrib/suggest/suggestController'; +import 'vs/editor/contrib/tokenization/tokenization'; import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode'; import 'vs/editor/contrib/wordHighlighter/wordHighlighter'; import 'vs/editor/contrib/wordOperations/wordOperations'; From 39bb8bb17d7b2ba17c3742a6ab058295b4c163da Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 13:46:49 +0100 Subject: [PATCH 0383/1066] label service: more tests --- src/vs/workbench/services/label/common/labelService.ts | 1 - src/vs/workbench/services/label/test/label.test.ts | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 978f39eb281..7b2180920c1 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -77,7 +77,6 @@ function hasDriveLetter(path: string): boolean { return !!(isWindows && path && path[2] === ':'); } - class ResourceLabelFormattersHandler implements IWorkbenchContribution { constructor(@ILabelService labelService: ILabelService) { resourceLabelFormattersExtPoint.setHandler(extensions => { diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index fb20a96b09b..914b791249b 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -84,7 +84,16 @@ suite('URI Label', () => { separator: '/' } }); + labelService.registerFormatter({ + scheme: 'vscode', + authority: 'mi*', + formatting: { + label: 'third', + separator: '/' + } + }); + // Make sure the most specific authority is picked const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5'); assert.equal(labelService.getUriLabel(uri1, { relative: false }), 'second'); }); From e0005380556e28b0e4efb2c20b45a4efe4de61f2 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Jan 2019 14:12:31 +0100 Subject: [PATCH 0384/1066] list match widget shadow --- src/vs/base/browser/ui/list/listWidget.ts | 5 +++++ src/vs/platform/theme/common/styler.ts | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 21118a86412..279555123f6 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -733,6 +733,10 @@ export class DefaultStyleController implements IStyleController { content.push(`.monaco-list-type-filter { border-color: ${styles.listMatchesOutline}; }`); } + if (styles.listMatchesShadow) { + content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 2px ${styles.listMatchesShadow}; }`); + } + const newStyles = content.join('\n'); if (newStyles !== this.styleElement.innerHTML) { this.styleElement.innerHTML = newStyles; @@ -779,6 +783,7 @@ export interface IListStyles { listHoverOutline?: Color; listMatchesBackground?: Color; listMatchesOutline?: Color; + listMatchesShadow?: Color; } const defaultStyles: IListStyles = { diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 1cba060e906..533a0e8a32a 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; + listMatchesShadow?: ColorIdentifier; } export function attachListStyler(widget: IThemable, themeService: IThemeService, overrides?: IListStyleOverrides): IDisposable { @@ -248,7 +249,8 @@ export const defaultListStyles: IColorMapping = { listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, listMatchesBackground: blend2(editorFindMatchHighlight, editorWidgetBackground), - listMatchesOutline: editorFindMatchHighlightBorder + listMatchesOutline: focusBorder, + listMatchesShadow: widgetShadow }; export interface IButtonStyleOverrides extends IStyleOverrides { From f20fb4e7c907a5835f4bc9c5680614220efabf7c Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 15:19:23 +0100 Subject: [PATCH 0385/1066] fix formatter finding --- src/vs/workbench/services/label/common/labelService.ts | 4 ++-- src/vs/workbench/services/label/test/label.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 7b2180920c1..b6c41954054 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -107,7 +107,7 @@ export class LabelService implements ILabelService { this.formatters.forEach(formatter => { if (formatter.scheme === resource.scheme) { - if (!bestResult) { + if (!bestResult && !formatter.authority) { bestResult = formatter; return; } @@ -115,7 +115,7 @@ export class LabelService implements ILabelService { return; } - if (match(formatter.authority, resource.authority) && (!bestResult.authority || formatter.authority.length > bestResult.authority.length)) { + if (match(formatter.authority, resource.authority) && (!bestResult || !bestResult.authority || formatter.authority.length > bestResult.authority.length)) { bestResult = formatter; } } diff --git a/src/vs/workbench/services/label/test/label.test.ts b/src/vs/workbench/services/label/test/label.test.ts index 914b791249b..233d942bc6c 100644 --- a/src/vs/workbench/services/label/test/label.test.ts +++ b/src/vs/workbench/services/label/test/label.test.ts @@ -70,7 +70,7 @@ suite('URI Label', () => { test('mulitple authority', function () { labelService.registerFormatter({ scheme: 'vscode', - authority: 'micro*', + authority: 'not_matching_but_long', formatting: { label: 'first', separator: '/' From b58125cc9bd6a849f223ab2e4f0238dd31d77abc Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 16:31:49 +0100 Subject: [PATCH 0386/1066] explorer: introduce cut action fixes #61110 --- .../fileActions.contribution.ts | 21 ++++++++++++++++++- .../files/electron-browser/fileActions.ts | 14 ++++++++++++- 2 files changed, 33 insertions(+), 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 e00908e44a2..9dfb73a25a5 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, ShowOpenedFileInNewWindow, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler } from 'vs/workbench/parts/files/electron-browser/fileActions'; +import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, ShowOpenedFileInNewWindow, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler } from 'vs/workbench/parts/files/electron-browser/fileActions'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/parts/files/electron-browser/saveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -92,6 +92,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: deleteFileHandler }); +const CUT_FILE_ID = 'filesExplorer.cut'; +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: CUT_FILE_ID, + weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerRootContext.toNegated()), + primary: KeyMod.CtrlCmd | KeyCode.KEY_X, + handler: cutFileHandler, +}); + const COPY_FILE_ID = 'filesExplorer.copy'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: COPY_FILE_ID, @@ -406,6 +415,16 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { when: ContextKeyExpr.and(ExplorerFolderContext.toNegated(), ResourceContextKey.HasResource, WorkbenchListDoubleSelection) }); +MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { + group: '5_cutcopypaste', + order: 8, + command: { + id: CUT_FILE_ID, + title: nls.localize('cut', "Cut") + }, + when: ExplorerRootContext.toNegated() +}); + MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '5_cutcopypaste', order: 10, diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 77513d81495..bb96fe20597 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -433,6 +433,7 @@ class BaseDeleteFileAction extends BaseErrorReportingAction { } } +let pasteShouldMove = false; // Copy File/Folder class CopyFileAction extends BaseErrorReportingAction { @@ -448,6 +449,7 @@ class CopyFileAction extends BaseErrorReportingAction { // Write to clipboard as file/folder to copy this.clipboardService.writeResources(this.elements.map(e => e.resource)); + pasteShouldMove = false; return Promise.resolve(null); } @@ -492,7 +494,8 @@ class PasteFileAction extends BaseErrorReportingAction { const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory }); // Copy File - return this.fileService.copyFile(fileToPaste, targetFile).then(stat => { + const promise = pasteShouldMove ? this.fileService.moveFile(fileToPaste, targetFile) : this.fileService.copyFile(fileToPaste, targetFile); + return promise.then(stat => { if (!stat.isDirectory) { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); } @@ -1129,6 +1132,15 @@ export const copyFileHandler = (accessor: ServicesAccessor) => { return copyFileAction.run(); }; +export const cutFileHandler = (accessor: ServicesAccessor) => { + const listService = accessor.get(IListService); + const explorerContext = getContext(listService.lastFocusedList); + const clipboardService = accessor.get(IClipboardService); + const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; + clipboardService.writeResources(stats.map(s => s.resource)); + pasteShouldMove = true; +}; + export const pasteFileHandler = (accessor: ServicesAccessor) => { const instantationService = accessor.get(IInstantiationService); const listService = accessor.get(IListService); From da37926dd3b9c77fc1b6c7da991d0a77a417df33 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 16:57:31 +0100 Subject: [PATCH 0387/1066] fixes #66681 --- src/vs/workbench/common/resources.ts | 7 ------- .../files/electron-browser/fileActions.contribution.ts | 6 +++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/common/resources.ts b/src/vs/workbench/common/resources.ts index fc2d44459cb..c726f2ce067 100644 --- a/src/vs/workbench/common/resources.ts +++ b/src/vs/workbench/common/resources.ts @@ -9,7 +9,6 @@ import { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/cont import { IModeService } from 'vs/editor/common/services/modeService'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; export class ResourceContextKey extends Disposable implements IContextKey { @@ -20,7 +19,6 @@ export class ResourceContextKey extends Disposable implements IContextKey { static Extension = new RawContextKey('resourceExtname', undefined); static HasResource = new RawContextKey('resourceSet', false); static IsFileSystemResource = new RawContextKey('isFileSystemResource', false); - static IsFileSystemResourceOrUntitled = new RawContextKey('isFileSystemResourceOrUntitled', false); private readonly _resourceKey: IContextKey; private readonly _schemeKey: IContextKey; @@ -29,7 +27,6 @@ export class ResourceContextKey extends Disposable implements IContextKey { private readonly _extensionKey: IContextKey; private readonly _hasResource: IContextKey; private readonly _isFileSystemResource: IContextKey; - private readonly _isFileSystemResourceOrUntitled: IContextKey; constructor( @IContextKeyService contextKeyService: IContextKeyService, @@ -45,12 +42,10 @@ export class ResourceContextKey extends Disposable implements IContextKey { this._extensionKey = ResourceContextKey.Extension.bindTo(contextKeyService); this._hasResource = ResourceContextKey.HasResource.bindTo(contextKeyService); this._isFileSystemResource = ResourceContextKey.IsFileSystemResource.bindTo(contextKeyService); - this._isFileSystemResourceOrUntitled = ResourceContextKey.IsFileSystemResourceOrUntitled.bindTo(contextKeyService); this._register(_fileService.onDidChangeFileSystemProviderRegistrations(() => { const resource = this._resourceKey.get(); this._isFileSystemResource.set(Boolean(resource && _fileService.canHandleResource(resource))); - this._isFileSystemResourceOrUntitled.set(this._isFileSystemResource.get() || this._schemeKey.get() === Schemas.untitled); })); this._register(_modeService.onDidCreateMode(() => { @@ -68,7 +63,6 @@ export class ResourceContextKey extends Disposable implements IContextKey { this._extensionKey.set(value && paths.extname(value.fsPath)); this._hasResource.set(!!value); this._isFileSystemResource.set(value && this._fileService.canHandleResource(value)); - this._isFileSystemResourceOrUntitled.set(this._isFileSystemResource.get() || this._schemeKey.get() === Schemas.untitled); } } @@ -80,7 +74,6 @@ export class ResourceContextKey extends Disposable implements IContextKey { this._extensionKey.reset(); this._hasResource.reset(); this._isFileSystemResource.reset(); - this._isFileSystemResourceOrUntitled.reset(); } get(): URI | undefined { 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 9dfb73a25a5..f2b6c333c94 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -293,7 +293,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 20, command: compareResourceCommand, - when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, ResourceSelectedForCompareContext, WorkbenchListDoubleSelection.toNegated()) }); const selectForCompareCommand = { @@ -304,7 +304,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: selectForCompareCommand, - when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, WorkbenchListDoubleSelection.toNegated()) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection.toNegated()) }); const compareSelectedCommand = { @@ -315,7 +315,7 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ResourceContextKey.IsFileSystemResourceOrUntitled, WorkbenchListDoubleSelection) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { From 6e7372fa6914e16794a31a8742d084f6d184f3bb Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 17:04:12 +0100 Subject: [PATCH 0388/1066] make breakpoints extension point contribution dynamic #66574 --- src/vs/workbench/parts/debug/common/debugSchemas.ts | 1 + .../electron-browser/debugConfigurationManager.ts | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugSchemas.ts b/src/vs/workbench/parts/debug/common/debugSchemas.ts index 422154c1cb8..ef17e55eb79 100644 --- a/src/vs/workbench/parts/debug/common/debugSchemas.ts +++ b/src/vs/workbench/parts/debug/common/debugSchemas.ts @@ -111,6 +111,7 @@ export interface IRawBreakpointContribution { // breakpoints extension point #9037 export const breakpointsExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'breakpoints', + isDynamic: true, jsonSchema: { description: nls.localize('vscode.extension.contributes.breakpoints', 'Contributes breakpoints.'), type: 'array', diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index afee263f473..d1b7d1447bd 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -272,11 +272,12 @@ export class ConfigurationManager implements IConfigurationManager { this.setCompoundSchemaValues(); }); - breakpointsExtPoint.setHandler(extensions => { - extensions.forEach(ext => { - ext.value.forEach(breakpoints => { - this.breakpointModeIdsSet.add(breakpoints.language); - }); + breakpointsExtPoint.setHandler((extensions, delta) => { + delta.removed.forEach(removed => { + removed.value.forEach(breakpoints => this.breakpointModeIdsSet.delete(breakpoints.language)); + }); + delta.added.forEach(added => { + added.value.forEach(breakpoints => this.breakpointModeIdsSet.add(breakpoints.language)); }); }); From d9285bd3c5729cd0d4619d0c1cef8f89e7f9a1e7 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 17:10:38 +0100 Subject: [PATCH 0389/1066] make resourceLabelFormatters dynamic #66574 --- .../services/label/common/labelService.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index b6c41954054..2e6032e7a99 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -28,6 +28,7 @@ import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', + isDynamic: true, jsonSchema: { description: localize('vscode.extension.contributes.resourceLabelFormatters', 'Contributes resource label formatting rules.'), type: 'array', @@ -78,9 +79,16 @@ function hasDriveLetter(path: string): boolean { } class ResourceLabelFormattersHandler implements IWorkbenchContribution { + private formattersDisposables = new Map(); + constructor(@ILabelService labelService: ILabelService) { - resourceLabelFormattersExtPoint.setHandler(extensions => { - extensions.forEach(extension => extension.value.forEach(formatter => labelService.registerFormatter(formatter))); + resourceLabelFormattersExtPoint.setHandler((extensions, delta) => { + delta.added.forEach(added => added.value.forEach(formatter => { + this.formattersDisposables.set(formatter, labelService.registerFormatter(formatter)); + })); + delta.removed.forEach(removed => removed.value.forEach(formatter => { + this.formattersDisposables.get(formatter).dispose(); + })); }); } } @@ -210,7 +218,10 @@ export class LabelService implements ILabelService { this._onDidRegisterFormatter.fire(); return { - dispose: () => this.formatters = this.formatters.filter(f => f !== formatter) + dispose: () => { + this.formatters = this.formatters.filter(f => f !== formatter); + this._onDidRegisterFormatter.fire(); + } }; } From 8c06c890a37ed76f42f86d773a9ac6be2ccf7a42 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 17:12:14 +0100 Subject: [PATCH 0390/1066] onDidRegisterFormatter -> onDidChangeFormatters --- src/vs/editor/standalone/browser/simpleServices.ts | 2 +- src/vs/platform/label/common/label.ts | 2 +- .../workbench/browser/parts/titlebar/menubarControl.ts | 2 +- .../workbench/browser/parts/titlebar/titlebarPart.ts | 2 +- .../parts/files/electron-browser/views/explorerView.ts | 4 ++-- .../parts/welcome/page/electron-browser/welcomePage.ts | 2 +- src/vs/workbench/services/label/common/labelService.ts | 10 +++++----- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index c8247a83871..b463eae52f8 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -612,7 +612,7 @@ export class SimpleUriLabelService implements ILabelService { _serviceBrand: any; private readonly _onDidRegisterFormatter = new Emitter(); - public readonly onDidRegisterFormatter: Event = this._onDidRegisterFormatter.event; + public readonly onDidChangeFormatters: Event = this._onDidRegisterFormatter.event; public getUriLabel(resource: URI, options?: { relative?: boolean, forceNoTildify?: boolean }): string { if (resource.scheme === 'file') { diff --git a/src/vs/platform/label/common/label.ts b/src/vs/platform/label/common/label.ts index 5846fd3796e..6f018648f98 100644 --- a/src/vs/platform/label/common/label.ts +++ b/src/vs/platform/label/common/label.ts @@ -26,7 +26,7 @@ export interface ILabelService { getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string; getHostLabel(): string; registerFormatter(formatter: ResourceLabelFormatter): IDisposable; - onDidRegisterFormatter: Event; + onDidChangeFormatters: Event; } export interface ResourceLabelFormatter { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index dee1daa5293..121bf09bb82 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -264,7 +264,7 @@ export class MenubarControl extends Disposable { } // Update recent menu items on formatter registration - this._register(this.labelService.onDidRegisterFormatter(() => { this.onRecentlyOpenedChange(); })); + this._register(this.labelService.onDidChangeFormatters(() => { this.onRecentlyOpenedChange(); })); } private doUpdateMenubar(firstTime: boolean): void { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 68ef9ee3614..93d58329d84 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -90,7 +90,7 @@ export class TitlebarPart extends Part implements ITitleService { this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.doUpdateTitle())); this._register(this.contextService.onDidChangeWorkbenchState(() => this.doUpdateTitle())); this._register(this.contextService.onDidChangeWorkspaceName(() => this.doUpdateTitle())); - this._register(this.labelService.onDidRegisterFormatter(() => this.doUpdateTitle())); + this._register(this.labelService.onDidChangeFormatters(() => this.doUpdateTitle())); } private onBlur(): void { 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 ac1272523f5..dda3b85aa34 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -141,7 +141,7 @@ export class ExplorerView extends ViewletPanel { }; this.disposables.push(this.contextService.onDidChangeWorkspaceName(setHeader)); - this.disposables.push(this.labelService.onDidRegisterFormatter(setHeader)); + this.disposables.push(this.labelService.onDidChangeFormatters(setHeader)); setHeader(); } @@ -158,7 +158,7 @@ export class ExplorerView extends ViewletPanel { } this.disposables.push(this.contextService.onDidChangeWorkbenchState(() => this.setTreeInput())); - this.disposables.push(this.labelService.onDidRegisterFormatter(() => { + this.disposables.push(this.labelService.onDidChangeFormatters(() => { this._onDidChangeTitleArea.fire(); this.refresh(); })); 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 a4fa09d9c34..0a127775cae 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -322,7 +322,7 @@ class WelcomePage { ul.append(...listEntries, moreRecent); }; updateEntries(); - this.disposables.push(this.labelService.onDidRegisterFormatter(updateEntries)); + this.disposables.push(this.labelService.onDidChangeFormatters(updateEntries)); }).then(undefined, onUnexpectedError); this.addExtensionList(container, '.extensionPackList', extensionPacks, extensionPackStrings); diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 2e6032e7a99..dfab0c043e7 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -98,7 +98,7 @@ export class LabelService implements ILabelService { _serviceBrand: any; private formatters: ResourceLabelFormatter[] = []; - private readonly _onDidRegisterFormatter = new Emitter(); + private readonly _onDidChangeFormatters = new Emitter(); constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -106,8 +106,8 @@ export class LabelService implements ILabelService { @IWindowService private readonly windowService: IWindowService ) { } - get onDidRegisterFormatter(): Event { - return this._onDidRegisterFormatter.event; + get onDidChangeFormatters(): Event { + return this._onDidChangeFormatters.event; } findFormatting(resource: URI): ResourceLabelFormatting | undefined { @@ -215,12 +215,12 @@ export class LabelService implements ILabelService { registerFormatter(formatter: ResourceLabelFormatter): IDisposable { this.formatters.push(formatter); - this._onDidRegisterFormatter.fire(); + this._onDidChangeFormatters.fire(); return { dispose: () => { this.formatters = this.formatters.filter(f => f !== formatter); - this._onDidRegisterFormatter.fire(); + this._onDidChangeFormatters.fire(); } }; } From ae72d6983d66a152bf0a65435d5a5ba16bde383b Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Jan 2019 17:12:12 +0100 Subject: [PATCH 0391/1066] Handle added/removed extensions in bulk --- src/vs/workbench/api/node/extHost.protocol.ts | 3 +- .../api/node/extHostExtensionService.ts | 11 +- .../extensionHostProcessManager.ts | 8 +- .../electron-browser/extensionService.ts | 144 ++++++++++++------ .../node/extensionDescriptionRegistry.ts | 12 +- 5 files changed, 107 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index fdaa10614ad..9a26e5fa431 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -741,8 +741,7 @@ export interface ExtHostExtensionServiceShape { $activateByEvent(activationEvent: string): Promise; $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise; - $addExtension(extension: IExtensionDescription): Promise; - $removeExtension(extension: ExtensionIdentifier): Promise; + $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise; $test_latency(n: number): Promise; $test_up(b: Buffer): Promise; diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index fe143a9989f..60d464dc504 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -669,14 +669,9 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { ); } - public $addExtension(extension: IExtensionDescription): Promise { - (extension).extensionLocation = URI.revive(extension.extensionLocation); - this._registry.add(extension); - return Promise.resolve(undefined); - } - - public $removeExtension(extensionId: ExtensionIdentifier): Promise { - this._registry.remove(extensionId); + public $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + this._registry.deltaExtensions(toAdd, toRemove); return Promise.resolve(undefined); } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index f108a8b8e57..114d60030dc 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -249,12 +249,8 @@ export class ExtensionHostProcessManager extends Disposable { return this._extensionHostProcessProxy.then(proxy => proxy.value.$startExtensionHost(enabledExtensionIds)); } - public addExtension(extension: IExtensionDescription): Promise { - return this._extensionHostProcessProxy.then(proxy => proxy.value.$addExtension(extension)); - } - - public removeExtension(extensionId: ExtensionIdentifier): Promise { - return this._extensionHostProcessProxy.then(proxy => proxy.value.$removeExtension(extensionId)); + public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + return this._extensionHostProcessProxy.then(proxy => proxy.value.$deltaExtensions(toAdd, toRemove)); } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index dd47be70fc5..173b7faf643 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -49,6 +49,13 @@ function allowProposedApiFromProduct(id: ExtensionIdentifier): boolean { return productAllowProposedApi.has(ExtensionIdentifier.toKey(id)); } +class DeltaExtensionsQueueItem { + constructor( + public readonly toAdd: IExtension[], + public readonly toRemove: string[] + ) { } +} + export class ExtensionService extends Disposable implements IExtensionService { public _serviceBrand: any; @@ -60,6 +67,7 @@ export class ExtensionService extends Disposable implements IExtensionService { private readonly _extensionsMessages: Map; private _allRequestedActivateEvents: { [activationEvent: string]: boolean; }; private readonly _extensionScanner: CachedExtensionScanner; + private _deltaExtensionsQueue: DeltaExtensionsQueueItem[]; private readonly _onDidRegisterExtensions: Emitter = this._register(new Emitter()); public readonly onDidRegisterExtensions = this._onDidRegisterExtensions.event; @@ -97,6 +105,7 @@ export class ExtensionService extends Disposable implements IExtensionService { this._extensionsMessages = new Map(); this._allRequestedActivateEvents = Object.create(null); this._extensionScanner = this._instantiationService.createInstance(CachedExtensionScanner); + this._deltaExtensionsQueue = []; this._extensionHostProcessManagers = []; this._extensionHostActiveExtensions = new Map(); @@ -115,60 +124,121 @@ export class ExtensionService extends Disposable implements IExtensionService { } this._extensionEnablementService.onEnablementChanged((extensions) => { + let toAdd: IExtension[] = []; + let toRemove: string[] = []; for (const extension of extensions) { if (this._extensionEnablementService.isEnabled(extension)) { // an extension has been enabled - this._addExtension(extension); + toAdd.push(extension); } else { // an extension has been disabled - this._removeExtension(extension.identifier.id); + toRemove.push(extension.identifier.id); } } + this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); }); this._extensionManagementService.onDidInstallExtension((event) => { if (event.local) { // an extension has been installed - this._addExtension(event.local); + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); } }); this._extensionManagementService.onDidUninstallExtension((event) => { if (!event.error) { // an extension has been uninstalled - this._removeExtension(event.identifier.id); + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([], [event.identifier.id])); } }); } - private async _removeExtension(extensionId: string): Promise { - const extensionDescription = this._registry.getExtensionDescription(extensionId); - if (!extensionDescription) { - // ignore disabling an extension which is not running + private _inHandleDeltaExtensions = false; + private async _handleDeltaExtensions(item: DeltaExtensionsQueueItem): Promise { + this._deltaExtensionsQueue.push(item); + if (this._inHandleDeltaExtensions) { + // Let the current item finish, the new one will be picked up return; } - if (!this._canRemoveExtension(extensionDescription)) { - // uses non-dynamic extension point or is activated - return; - } - - // Remove the extension from the local registry and from the extension host - this._registry.remove(extensionDescription.identifier); - - this._rehandleExtensionPoints(extensionDescription); - - if (this._extensionHostProcessManagers.length > 0) { - await this._extensionHostProcessManagers[0].removeExtension(extensionDescription.identifier); + while (this._deltaExtensionsQueue.length > 0) { + const item = this._deltaExtensionsQueue.shift(); + try { + this._inHandleDeltaExtensions = true; + await this._deltaExtensions(item.toAdd, item.toRemove); + } finally { + this._inHandleDeltaExtensions = false; + } } } - private _rehandleExtensionPoints(extensionDescription: IExtensionDescription): void { + private async _deltaExtensions(_toAdd: IExtension[], _toRemove: string[]): Promise { + if (this._windowService.getConfiguration().remoteAuthority) { + return; + } + + let toAdd: IExtensionDescription[] = []; + for (let i = 0, len = _toAdd.length; i < len; i++) { + const extension = _toAdd[i]; + + if (extension.location.scheme !== Schemas.file) { + continue; + } + + const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); + if (!extensionDescription || !this._usesOnlyDynamicExtensionPoints(extensionDescription)) { + // uses non-dynamic extension point + continue; + } + + toAdd.push(extensionDescription); + } + + let toRemove: IExtensionDescription[] = []; + for (let i = 0, len = _toRemove.length; i < len; i++) { + const extensionId = _toRemove[i]; + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + // ignore disabling/uninstalling an extension which is not running + continue; + } + + if (!this._canRemoveExtension(extensionDescription)) { + // uses non-dynamic extension point or is activated + continue; + } + + toRemove.push(extensionDescription); + } + + if (toAdd.length === 0 && toRemove.length === 0) { + return; + } + + // Update the local registry + this._registry.deltaExtensions(toAdd, toRemove.map(e => e.identifier)); + + // Update extension points + this._rehandleExtensionPoints(([]).concat(toAdd).concat(toRemove)); + + // Update the extension host + if (this._extensionHostProcessManagers.length > 0) { + await this._extensionHostProcessManagers[0].deltaExtensions(toAdd, toRemove.map(e => e.identifier)); + } + + for (let i = 0; i < toAdd.length; i++) { + this._activateAddedExtensionIfNeeded(toAdd[i]); + } + } + + private _rehandleExtensionPoints(extensionDescriptions: IExtensionDescription[]): void { const affectedExtensionPoints: { [extPointName: string]: boolean; } = Object.create(null); - if (extensionDescription.contributes) { - for (let extPointName in extensionDescription.contributes) { - if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { - affectedExtensionPoints[extPointName] = true; + for (let extensionDescription of extensionDescriptions) { + if (extensionDescription.contributes) { + for (let extPointName in extensionDescription.contributes) { + if (hasOwnProperty.call(extensionDescription.contributes, extPointName)) { + affectedExtensionPoints[extPointName] = true; + } } } } @@ -182,7 +252,6 @@ export class ExtensionService extends Disposable implements IExtensionService { ExtensionService._handleExtensionPoint(extensionPoints[i], availableExtensions, messageHandler); } } - } private _usesOnlyDynamicExtensionPoints(extension: IExtensionDescription): boolean { @@ -210,28 +279,7 @@ export class ExtensionService extends Disposable implements IExtensionService { return this._usesOnlyDynamicExtensionPoints(extension); } - private async _addExtension(extension: IExtension): Promise { - if (this._windowService.getConfiguration().remoteAuthority) { - return; - } - if (extension.location.scheme !== Schemas.file) { - return; - } - - const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); - if (!extensionDescription || !this._usesOnlyDynamicExtensionPoints(extensionDescription)) { - // uses non-dynamic extension point - return; - } - - // Add the extension to the local registry and to the extension host - this._registry.add(extensionDescription); - - this._rehandleExtensionPoints(extensionDescription); - - if (this._extensionHostProcessManagers.length > 0) { - await this._extensionHostProcessManagers[0].addExtension(extensionDescription); - } + private async _activateAddedExtensionIfNeeded(extensionDescription: IExtensionDescription): Promise { let shouldActivate = false; let shouldActivateReason: string | null = null; diff --git a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts index fc345fcc52c..ad385a196c9 100644 --- a/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts +++ b/src/vs/workbench/services/extensions/node/extensionDescriptionRegistry.ts @@ -55,13 +55,11 @@ export class ExtensionDescriptionRegistry { this._initialize(); } - public remove(extensionId: ExtensionIdentifier): void { - this._extensionDescriptions = this._extensionDescriptions.filter(extension => !ExtensionIdentifier.equals(extension.identifier, extensionId)); - this._initialize(); - } - - public add(extension: IExtensionDescription): void { - this._extensionDescriptions.push(extension); + public deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]) { + this._extensionDescriptions = this._extensionDescriptions.concat(toAdd); + const toRemoveSet = new Set(); + toRemove.forEach(extensionId => toRemoveSet.add(ExtensionIdentifier.toKey(extensionId))); + this._extensionDescriptions = this._extensionDescriptions.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier))); this._initialize(); } From e7ee83ef5621a3d1fb44c3e2c469fb6bceec3c40 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Fri, 18 Jan 2019 17:29:41 +0100 Subject: [PATCH 0392/1066] Emit an event when the set of available extensions has changed --- .../services/commands/test/common/commandService.test.ts | 1 + src/vs/workbench/services/extensions/common/extensions.ts | 5 +++++ .../services/extensions/electron-browser/extensionService.ts | 5 +++++ src/vs/workbench/test/workbenchTestServices.ts | 1 + 4 files changed, 12 insertions(+) diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index 62bf60ac6b9..e25c3f662ba 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -19,6 +19,7 @@ class SimpleExtensionService implements IExtensionService { return this._onDidRegisterExtensions.event; } onDidChangeExtensionsStatus = null!; + onDidChangeExtensions = null!; onWillActivateByEvent = null!; onDidChangeResponsiveChange = null!; activateByEvent(activationEvent: string): Promise { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 4c5b140c8c5..4057ccb4c56 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -143,6 +143,11 @@ export interface IExtensionService extends ICpuProfilerTarget { */ onDidChangeExtensionsStatus: Event; + /** + * Fired when the available extensions change (i.e. when extensions are added or removed). + */ + onDidChangeExtensions: Event; + /** * An event that is fired when activation happens. */ diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 173b7faf643..d61e59c3041 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -75,6 +75,9 @@ export class ExtensionService extends Disposable implements IExtensionService { private readonly _onDidChangeExtensionsStatus: Emitter = this._register(new Emitter()); public readonly onDidChangeExtensionsStatus: Event = this._onDidChangeExtensionsStatus.event; + private readonly _onDidChangeExtensions: Emitter = this._register(new Emitter()); + public readonly onDidChangeExtensions: Event = this._onDidChangeExtensions.event; + private readonly _onWillActivateByEvent = this._register(new Emitter()); public readonly onWillActivateByEvent: Event = this._onWillActivateByEvent.event; @@ -226,6 +229,8 @@ export class ExtensionService extends Disposable implements IExtensionService { await this._extensionHostProcessManagers[0].deltaExtensions(toAdd, toRemove.map(e => e.identifier)); } + this._onDidChangeExtensions.fire(undefined); + for (let i = 0; i < toAdd.length; i++) { this._activateAddedExtensionIfNeeded(toAdd[i]); } diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index fc7000c55a3..354243890f3 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -310,6 +310,7 @@ export class TestExtensionService implements IExtensionService { _serviceBrand: any; onDidRegisterExtensions: Event = Event.None; onDidChangeExtensionsStatus: Event = Event.None; + onDidChangeExtensions: Event = Event.None; onWillActivateByEvent: Event = Event.None; onDidChangeResponsiveChange: Event = Event.None; activateByEvent(_activationEvent: string): Promise { return Promise.resolve(undefined); } From ac1df80c6705957eba4077fdbf7f331fb52bfe7e Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 17:43:58 +0100 Subject: [PATCH 0393/1066] makde debuggers extension point contribution dynamic #66574 --- .../parts/debug/common/debugSchemas.ts | 1 + .../debugConfigurationManager.ts | 26 ++++++++++++++----- .../debug/electron-browser/debugService.ts | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debugSchemas.ts b/src/vs/workbench/parts/debug/common/debugSchemas.ts index ef17e55eb79..67b573e33d4 100644 --- a/src/vs/workbench/parts/debug/common/debugSchemas.ts +++ b/src/vs/workbench/parts/debug/common/debugSchemas.ts @@ -13,6 +13,7 @@ import { inputsSchema } from 'vs/workbench/services/configurationResolver/common // debuggers extension point export const debuggersExtPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'debuggers', + isDynamic: true, jsonSchema: { description: nls.localize('vscode.extension.contributes.debuggers', 'Contributes debug adapters.'), type: 'array', diff --git a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts index d1b7d1447bd..87c69d51e3f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugConfigurationManager.ts @@ -21,7 +21,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugAdapterTrackerFactory } from 'vs/workbench/parts/debug/common/debug'; +import { IDebugConfigurationProvider, ICompound, IDebugConfiguration, IConfig, IGlobalConfig, IConfigurationManager, ILaunch, IDebugAdapterDescriptorFactory, IDebugAdapter, ITerminalSettings, ITerminalLauncher, IDebugSession, IAdapterDescriptor, CONTEXT_DEBUG_CONFIGURATION_TYPE, IDebugAdapterFactory, IDebugAdapterTrackerFactory, IDebugService } from 'vs/workbench/parts/debug/common/debug'; import { Debugger } from 'vs/workbench/parts/debug/node/debugger'; import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -33,6 +33,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat import { launchSchema, debuggersExtPoint, breakpointsExtPoint } from 'vs/workbench/parts/debug/common/debugSchemas'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { onUnexpectedError } from 'vs/base/common/errors'; const jsonRegistry = Registry.as(JSONExtensions.JSONContribution); jsonRegistry.registerSchema(launchSchemaId, launchSchema); @@ -56,6 +57,7 @@ export class ConfigurationManager implements IConfigurationManager { private debugConfigurationTypeContext: IContextKey; constructor( + private debugService: IDebugService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -235,11 +237,11 @@ export class ConfigurationManager implements IConfigurationManager { } private registerListeners(lifecycleService: ILifecycleService): void { - debuggersExtPoint.setHandler((extensions) => { - extensions.forEach(extension => { - extension.value.forEach(rawAdapter => { + debuggersExtPoint.setHandler((extensions, delta) => { + delta.added.forEach(added => { + added.value.forEach(rawAdapter => { if (!rawAdapter.type || (typeof rawAdapter.type !== 'string')) { - extension.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); + added.collector.error(nls.localize('debugNoType', "Debugger 'type' can not be omitted and must be of type 'string'.")); } if (rawAdapter.enableBreakpointsFor) { rawAdapter.enableBreakpointsFor.languageIds.forEach(modeId => { @@ -249,9 +251,19 @@ export class ConfigurationManager implements IConfigurationManager { const duplicate = this.getDebugger(rawAdapter.type); if (duplicate) { - duplicate.merge(rawAdapter, extension.description); + duplicate.merge(rawAdapter, added.description); } else { - this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, extension.description)); + this.debuggers.push(this.instantiationService.createInstance(Debugger, this, rawAdapter, added.description)); + } + }); + }); + delta.removed.forEach(removed => { + const removedTypes = removed.value.map(rawAdapter => rawAdapter.type); + this.debuggers = this.debuggers.filter(d => removedTypes.indexOf(d.type) === -1); + this.debugService.getModel().getSessions().forEach(s => { + // Stop sessions if their debugger has been removed + if (removedTypes.indexOf(s.configuration.type) >= 0) { + this.debugService.stopSession(s).then(undefined, onUnexpectedError); } }); }); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugService.ts b/src/vs/workbench/parts/debug/electron-browser/debugService.ts index 2e17633e4a1..8af829fa6e7 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugService.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugService.ts @@ -119,7 +119,7 @@ export class DebugService implements IDebugService { this._onWillNewSession = new Emitter(); this._onDidEndSession = new Emitter(); - this.configurationManager = this.instantiationService.createInstance(ConfigurationManager); + this.configurationManager = this.instantiationService.createInstance(ConfigurationManager, this); this.toDispose.push(this.configurationManager); this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService); From 82de4c8b4ff79c293714bf22e5a9fc2a1f6287ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 12:40:24 +0100 Subject: [PATCH 0394/1066] #66653 merge view and view container extension point into one file --- .../browser/viewsContainersExtensionPoint.ts | 220 ------------------ .../api/browser/viewsExtensionPoint.ts | 203 +++++++++++++++- .../electron-browser/mainThreadTreeViews.ts | 4 +- .../browser/parts/views/customView.ts | 15 +- src/vs/workbench/common/views.ts | 2 +- src/vs/workbench/workbench.main.ts | 1 - 6 files changed, 208 insertions(+), 237 deletions(-) delete mode 100644 src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts deleted file mode 100644 index b732ccd81d6..00000000000 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ /dev/null @@ -1,220 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { localize } from 'vs/nls'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import * as resources from 'vs/base/common/resources'; -import { createCSSRule } from 'vs/base/browser/dom'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import { ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions, ShowViewletAction } from 'vs/workbench/browser/viewlet'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPartService } from 'vs/workbench/services/part/common/partService'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { Extensions as ViewContainerExtensions, IViewContainersRegistry, TEST_VIEW_CONTAINER_ID } from 'vs/workbench/common/views'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; -import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { forEach } from 'vs/base/common/collections'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; -import { URI } from 'vs/base/common/uri'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; - -export interface IUserFriendlyViewsContainerDescriptor { - id: string; - title: string; - icon: string; -} - -export interface IUserFriendlyViewsContainerDescriptor2 { - id: string; - title: string; - icon: URI; -} - -const viewsContainerSchema: IJSONSchema = { - type: 'object', - properties: { - id: { - description: localize({ key: 'vscode.extension.contributes.views.containers.id', comment: ['Contribution refers to those that an extension contributes to VS Code through an extension/contribution point. '] }, "Unique id used to identify the container in which views can be contributed using 'views' contribution point"), - type: 'string', - pattern: '^[a-zA-Z0-9_-]+$' - }, - title: { - description: localize('vscode.extension.contributes.views.containers.title', 'Human readable string used to render the container'), - type: 'string' - }, - icon: { - description: localize('vscode.extension.contributes.views.containers.icon', "Path to the container icon. Icons are 24x24 centered on a 50x40 block and have a fill color of 'rgb(215, 218, 224)' or '#d7dae0'. It is recommended that icons be in SVG, though any image file type is accepted."), - type: 'string' - } - } -}; - -export const viewsContainersContribution: IJSONSchema = { - description: localize('vscode.extension.contributes.viewsContainers', 'Contributes views containers to the editor'), - type: 'object', - properties: { - 'activitybar': { - description: localize('views.container.activitybar', "Contribute views containers to Activity Bar"), - type: 'array', - items: viewsContainerSchema - } - } -}; - -export const viewsContainersExtensionPoint: IExtensionPoint<{ [loc: string]: IUserFriendlyViewsContainerDescriptor[] }> = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: IUserFriendlyViewsContainerDescriptor[] }>({ - extensionPoint: 'viewsContainers', - jsonSchema: viewsContainersContribution -}); - -const TEST_VIEW_CONTAINER_ORDER = 6; - -class ViewsContainersExtensionHandler implements IWorkbenchContribution { - - constructor() { - this.registerTestViewContainer(); - this.handleAndRegisterCustomViewContainers(); - } - - private registerTestViewContainer(): void { - const title = localize('test', "Test"); - const cssClass = `extensionViewlet-test`; - const icon = URI.parse(require.toUrl('./media/test.svg')); - - this.registerCustomViewlet({ id: TEST_VIEW_CONTAINER_ID, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined); - } - - private handleAndRegisterCustomViewContainers() { - let order = TEST_VIEW_CONTAINER_ORDER + 1; - viewsContainersExtensionPoint.setHandler((extensions) => { - for (let extension of extensions) { - const { value, collector } = extension; - forEach(value, entry => { - if (!this.isValidViewsContainer(entry.value, collector)) { - return; - } - switch (entry.key) { - case 'activitybar': - order = this.registerCustomViewContainers(entry.value, extension.description, order); - break; - } - }); - } - }); - } - - private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { - if (!Array.isArray(viewsContainersDescriptors)) { - collector.error(localize('requirearray', "views containers must be an array")); - return false; - } - - for (let descriptor of viewsContainersDescriptors) { - if (typeof descriptor.id !== 'string') { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - return false; - } - if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { - collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); - return false; - } - if (typeof descriptor.title !== 'string') { - collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); - return false; - } - if (typeof descriptor.icon !== 'string') { - collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); - return false; - } - } - - return true; - } - - private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number): number { - containers.forEach(descriptor => { - const cssClass = `extensionViewlet-${descriptor.id}`; - const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); - this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, order++, cssClass, extension.identifier); - }); - return order; - } - - private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor2, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): void { - const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); - const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); - const id = descriptor.id; - - if (!viewletRegistry.getViewlet(id)) { - - viewContainersRegistry.registerViewContainer(id, extensionId); - - // Register as viewlet - class CustomViewlet extends ViewContainerViewlet { - constructor( - @IConfigurationService configurationService: IConfigurationService, - @IPartService partService: IPartService, - @ITelemetryService telemetryService: ITelemetryService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IStorageService storageService: IStorageService, - @IEditorService editorService: IEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, - @IContextMenuService contextMenuService: IContextMenuService, - @IExtensionService extensionService: IExtensionService - ) { - super(id, `${id}.state`, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); - } - } - const viewletDescriptor = new ViewletDescriptor( - CustomViewlet, - id, - descriptor.title, - cssClass, - order, - descriptor.icon - ); - - viewletRegistry.registerViewlet(viewletDescriptor); - - // Register Action to Open Viewlet - class OpenCustomViewletAction extends ShowViewletAction { - constructor( - id: string, label: string, - @IViewletService viewletService: IViewletService, - @IEditorGroupsService editorGroupService: IEditorGroupsService, - @IPartService partService: IPartService - ) { - super(id, label, id, viewletService, editorGroupService, partService); - } - } - const registry = Registry.as(ActionExtensions.WorkbenchActions); - registry.registerWorkbenchAction( - new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", descriptor.title)), - 'View: Show {0}', - localize('view', "View") - ); - - // Generate CSS to show the icon in the activity bar - const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${cssClass}`; - createCSSRule(iconClass, `-webkit-mask: url('${descriptor.icon}') no-repeat 50% 50%`); - } - - } -} - -const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ViewsContainersExtensionHandler, LifecyclePhase.Starting); diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 99ea9a0a611..c36f49edfb4 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -6,12 +6,12 @@ import { localize } from 'vs/nls'; import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ViewContainer, ViewsRegistry, ICustomViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; +import { ViewContainer, ViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID } from 'vs/workbench/common/views'; import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; -import { viewsContainersExtensionPoint } from 'vs/workbench/api/browser/viewsContainersExtensionPoint'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -20,6 +20,60 @@ import { VIEWLET_ID as EXPLORER } from 'vs/workbench/parts/files/common/files'; import { VIEWLET_ID as SCM } from 'vs/workbench/parts/scm/common/scm'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/parts/debug/common/debug'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { URI } from 'vs/base/common/uri'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; +import { IExtensionDescription, IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IPartService } from 'vs/workbench/services/part/common/partService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { createCSSRule } from 'vs/base/browser/dom'; + +export interface IUserFriendlyViewsContainerDescriptor { + id: string; + title: string; + icon: string; +} + +const viewsContainerSchema: IJSONSchema = { + type: 'object', + properties: { + id: { + description: localize({ key: 'vscode.extension.contributes.views.containers.id', comment: ['Contribution refers to those that an extension contributes to VS Code through an extension/contribution point. '] }, "Unique id used to identify the container in which views can be contributed using 'views' contribution point"), + type: 'string', + pattern: '^[a-zA-Z0-9_-]+$' + }, + title: { + description: localize('vscode.extension.contributes.views.containers.title', 'Human readable string used to render the container'), + type: 'string' + }, + icon: { + description: localize('vscode.extension.contributes.views.containers.icon', "Path to the container icon. Icons are 24x24 centered on a 50x40 block and have a fill color of 'rgb(215, 218, 224)' or '#d7dae0'. It is recommended that icons be in SVG, though any image file type is accepted."), + type: 'string' + } + } +}; + +export const viewsContainersContribution: IJSONSchema = { + description: localize('vscode.extension.contributes.viewsContainers', 'Contributes views containers to the editor'), + type: 'object', + properties: { + 'activitybar': { + description: localize('views.container.activitybar', "Contribute views containers to Activity Bar"), + type: 'array', + items: viewsContainerSchema + } + } +}; interface IUserFriendlyViewDescriptor { id: string; @@ -82,6 +136,16 @@ const viewsContribution: IJSONSchema = { } }; +export interface ICustomViewDescriptor extends ITreeViewDescriptor { + readonly extensionId: ExtensionIdentifier; +} + +type ViewContainerExtensionPointType = { [loc: string]: IUserFriendlyViewsContainerDescriptor[] }; +const viewsContainersExtensionPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ + extensionPoint: 'viewsContainers', + jsonSchema: viewsContainersContribution +}); + type ViewExtensionPointType = { [loc: string]: IUserFriendlyViewDescriptor[] }; const viewsExtensionPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'views', @@ -90,7 +154,8 @@ const viewsExtensionPoint: IExtensionPoint = ExtensionsR isDynamic: true }); -class ViewsContainersExtensionHandler implements IWorkbenchContribution { +const TEST_VIEW_CONTAINER_ORDER = 6; +class ViewsExtensionHandler implements IWorkbenchContribution { private viewContainersRegistry: IViewContainersRegistry; @@ -98,9 +163,137 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { @IInstantiationService private readonly instantiationService: IInstantiationService ) { this.viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + this.handleAndRegisterCustomViewContainers(); this.handleAndRegisterCustomViews(); } + private handleAndRegisterCustomViewContainers() { + this.registerTestViewContainer(); + + let order = TEST_VIEW_CONTAINER_ORDER + 1; + viewsContainersExtensionPoint.setHandler((extensions) => { + for (let extension of extensions) { + const { value, collector } = extension; + forEach(value, entry => { + if (!this.isValidViewsContainer(entry.value, collector)) { + return; + } + switch (entry.key) { + case 'activitybar': + order = this.registerCustomViewContainers(entry.value, extension.description, order); + break; + } + }); + } + }); + } + + private registerTestViewContainer(): void { + const title = localize('test', "Test"); + const cssClass = `extensionViewlet-test`; + const icon = URI.parse(require.toUrl('./media/test.svg')); + + this.registerCustomViewlet(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined); + } + + private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { + if (!Array.isArray(viewsContainersDescriptors)) { + collector.error(localize('viewcontainer requirearray', "views containers must be an array")); + return false; + } + + for (let descriptor of viewsContainersDescriptors) { + if (typeof descriptor.id !== 'string') { + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + return false; + } + if (!(/^[a-z0-9_-]+$/i.test(descriptor.id))) { + collector.error(localize('requireidstring', "property `{0}` is mandatory and must be of type `string`. Only alphanumeric characters, '_', and '-' are allowed.", 'id')); + return false; + } + if (typeof descriptor.title !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'title')); + return false; + } + if (typeof descriptor.icon !== 'string') { + collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'icon')); + return false; + } + } + + return true; + } + + private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number): number { + containers.forEach(descriptor => { + const cssClass = `extensionViewlet-${descriptor.id}`; + const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); + this.registerCustomViewlet(`workbench.view.extension.${descriptor.id}`, descriptor.title, icon, order++, cssClass, extension.identifier); + }); + return order; + } + + private registerCustomViewlet(id: string, title: string, icon: URI, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): void { + const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + + if (!viewletRegistry.getViewlet(id)) { + + viewContainersRegistry.registerViewContainer(id, extensionId); + + // Register as viewlet + class CustomViewlet extends ViewContainerViewlet { + constructor( + @IConfigurationService configurationService: IConfigurationService, + @IPartService partService: IPartService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IEditorService editorService: IEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(id, `${id}.state`, true, configurationService, partService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + } + } + const viewletDescriptor = new ViewletDescriptor( + CustomViewlet, + id, + title, + cssClass, + order, + icon + ); + + viewletRegistry.registerViewlet(viewletDescriptor); + + // Register Action to Open Viewlet + class OpenCustomViewletAction extends ShowViewletAction { + constructor( + id: string, label: string, + @IViewletService viewletService: IViewletService, + @IEditorGroupsService editorGroupService: IEditorGroupsService, + @IPartService partService: IPartService + ) { + super(id, label, id, viewletService, editorGroupService, partService); + } + } + const registry = Registry.as(ActionExtensions.WorkbenchActions); + registry.registerWorkbenchAction( + new SyncActionDescriptor(OpenCustomViewletAction, id, localize('showViewlet', "Show {0}", title)), + 'View: Show {0}', + localize('view', "View") + ); + + // Generate CSS to show the icon in the activity bar + const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${cssClass}`; + createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`); + } + + } + private handleAndRegisterCustomViews() { viewsExtensionPoint.setHandler((extensions, { added, removed }) => { if (removed.length) { @@ -139,7 +332,7 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { return null; } - const viewDescriptor = { + const viewDescriptor = { id: item.id, name: item.name, ctor: CustomTreeViewPanel, @@ -215,4 +408,4 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(ViewsContainersExtensionHandler, LifecyclePhase.Starting); \ No newline at end of file +workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting); \ No newline at end of file diff --git a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts index a2237755d84..c3617f2bb85 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTreeViews.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, ViewsRegistry, ICustomViewDescriptor, IRevealOptions } from 'vs/workbench/common/views'; +import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, ViewsRegistry, ITreeViewDescriptor, IRevealOptions } from 'vs/workbench/common/views'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { distinct } from 'vs/base/common/arrays'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -110,7 +110,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie } private getTreeView(treeViewId: string): ITreeView { - const viewDescriptor: ICustomViewDescriptor = ViewsRegistry.getView(treeViewId); + const viewDescriptor: ITreeViewDescriptor = ViewsRegistry.getView(treeViewId); return viewDescriptor ? viewDescriptor.treeView : null; } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 456eb50130b..daebf0af817 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ICustomViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; +import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views'; import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -58,7 +58,7 @@ export class CustomTreeViewPanel extends ViewletPanel { @IViewsService viewsService: IViewsService, ) { super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService); - const { treeView } = (ViewsRegistry.getView(options.id)); + const { treeView } = (ViewsRegistry.getView(options.id)); this.treeView = treeView; this.treeView.onDidChangeActions(() => this.updateActions(), this, this.disposables); this.disposables.push(toDisposable(() => this.treeView.setVisibility(false))); @@ -210,7 +210,7 @@ export class CustomTreeView extends Disposable implements ITreeView { constructor( private id: string, - private container: ViewContainer, + private viewContainer: ViewContainer, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly themeService: IWorkbenchThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -376,7 +376,7 @@ export class CustomTreeView extends Disposable implements ITreeView { const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined; const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id)); this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); - const dataSource = this.instantiationService.createInstance(TreeDataSource, this, this.container); + const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task)); const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, menus, this.treeLabels, actionItemProvider); const controller = this.instantiationService.createInstance(TreeController, this.id, menus); this.tree = this._register(this.instantiationService.createInstance(FileIconThemableWorkbenchTree, this.treeContainer, { dataSource, renderer, controller }, {})); @@ -509,7 +509,7 @@ export class CustomTreeView extends Disposable implements ITreeView { private activate() { if (!this.activated) { this.createTree(); - this.progressService.withProgress({ location: this.container.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) + this.progressService.withProgress({ location: this.viewContainer.id }, () => this.extensionService.activateByEvent(`onView:${this.id}`)) .then(() => timeout(2000)) .then(() => { this.updateMessage(); @@ -569,8 +569,7 @@ class TreeDataSource implements IDataSource { constructor( private treeView: ITreeView, - private container: ViewContainer, - @IProgressService2 private readonly progressService: IProgressService2 + private withProgress: (task: Promise) => Promise ) { } @@ -584,7 +583,7 @@ class TreeDataSource implements IDataSource { getChildren(tree: ITree, node: ITreeItem): Promise { if (this.treeView.dataProvider) { - return this.progressService.withProgress({ location: this.container.id }, () => this.treeView.dataProvider.getChildren(node)); + return this.withProgress(this.treeView.dataProvider.getChildren(node)); } return Promise.resolve([]); } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 7b36e2170b5..6ae178230c2 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -294,7 +294,7 @@ export interface IRevealOptions { } -export interface ICustomViewDescriptor extends IViewDescriptor { +export interface ITreeViewDescriptor extends IViewDescriptor { readonly treeView: ITreeView; readonly extensionId: ExtensionIdentifier; diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index dc6ef56c636..1cb536f7767 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -20,7 +20,6 @@ import 'vs/platform/widget/browser/contextScopedHistoryWidget'; import 'vs/workbench/services/actions/electron-browser/menusExtensionPoint'; // Views -import 'vs/workbench/api/browser/viewsContainersExtensionPoint'; import 'vs/workbench/api/browser/viewsExtensionPoint'; // Localizations From 35742d5e402223f187b49315b8087163d479dca4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 15:47:55 +0100 Subject: [PATCH 0395/1066] #66653 Remove container from view descriptor --- .../api/browser/viewsExtensionPoint.ts | 3 +- src/vs/workbench/browser/parts/views/views.ts | 25 +++---- src/vs/workbench/common/views.ts | 70 ++++++++++--------- .../electron-browser/debug.contribution.ts | 10 +-- .../electron-browser/extensionsViewlet.ts | 13 +--- .../files/electron-browser/explorerViewlet.ts | 5 +- .../electron-browser/outline.contribution.ts | 3 +- .../test/browser/parts/views/views.test.ts | 34 +++++---- 8 files changed, 74 insertions(+), 89 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index c36f49edfb4..2a800bf880f 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -336,7 +336,6 @@ class ViewsExtensionHandler implements IWorkbenchContribution { id: item.id, name: item.name, ctor: CustomTreeViewPanel, - container, when: ContextKeyExpr.deserialize(item.when), canToggleVisibility: true, collapsed: this.showCollapsed(container), @@ -348,7 +347,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { viewIds.push(viewDescriptor.id); return viewDescriptor; })); - ViewsRegistry.registerViews(viewDescriptors); + ViewsRegistry.registerViews(viewDescriptors, container); }); } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index b0e7153d2f1..c39900cfe01 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -22,9 +22,9 @@ import { values } from 'vs/base/common/map'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { toggleClass, addClass } from 'vs/base/browser/dom'; -function filterViewEvent(container: ViewContainer, event: Event): Event { +function filterViewEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event { return Event.chain(event) - .map(views => views.filter(view => view.container === container)) + .map(({ views, viewContainer }) => viewContainer === container ? views : []) .filter(views => views.length > 0) .event; } @@ -529,11 +529,12 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDescriptorCollections = new Map(); this.activeViewContextKeys = new Map>(); - this.onDidRegisterViews(ViewsRegistry.getAllViews()); - this._register(ViewsRegistry.onViewsRegistered(views => this.onDidRegisterViews(views))); - const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); - viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer)); + viewContainersRegistry.all.forEach(viewContainer => { + this.onDidRegisterViews(viewContainer, ViewsRegistry.getViews(viewContainer)); + this.onDidRegisterViewContainer(viewContainer); + }); + this._register(ViewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer))); } @@ -542,9 +543,9 @@ export class ViewsService extends Disposable implements IViewsService { } openView(id: string, focus: boolean): Promise { - const viewDescriptor = ViewsRegistry.getView(id); - if (viewDescriptor) { - const viewletDescriptor = this.viewletService.getViewlet(viewDescriptor.container.id); + const viewContainer = ViewsRegistry.getViewContainer(id); + if (viewContainer) { + const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id); if (viewletDescriptor) { return this.viewletService.openViewlet(viewletDescriptor.id, focus) .then((viewlet: IViewsViewlet) => { @@ -572,9 +573,9 @@ export class ViewsService extends Disposable implements IViewsService { removed.forEach(viewDescriptor => this.getOrCreateActiveViewContextKey(viewDescriptor).set(false)); } - private onDidRegisterViews(viewDescriptors: IViewDescriptor[]): void { - for (const viewDescriptor of viewDescriptors) { - const viewlet = this.viewletService.getViewlet(viewDescriptor.container.id); + private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { + const viewlet = this.viewletService.getViewlet(container.id); + for (const viewDescriptor of views) { const command: ICommandAction = { id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 6ae178230c2..e4f720d6ab1 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -99,8 +99,6 @@ export interface IViewDescriptor { readonly name: string; - readonly container: ViewContainer; - // TODO@Sandeep do we really need this?! readonly ctor: any; @@ -128,52 +126,52 @@ export interface IViewDescriptorCollection { export interface IViewsRegistry { - readonly onViewsRegistered: Event; + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; - readonly onViewsDeregistered: Event; + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; - registerViews(views: IViewDescriptor[]): void; + registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; - deregisterViews(ids: string[], container: ViewContainer): void; + deregisterViews(ids: string[], viewContainer: ViewContainer): void; - getViews(loc: ViewContainer): IViewDescriptor[]; + getViews(viewContainer: ViewContainer): IViewDescriptor[]; getView(id: string): IViewDescriptor | null; - getAllViews(): IViewDescriptor[]; + getViewContainer(id: string): ViewContainer | null; } export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry { - private readonly _onViewsRegistered: Emitter = new Emitter(); - readonly onViewsRegistered: Event = this._onViewsRegistered.event; + private readonly _onViewsRegistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>(); + readonly onViewsRegistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsRegistered.event; - private readonly _onViewsDeregistered: Emitter = new Emitter(); - readonly onViewsDeregistered: Event = this._onViewsDeregistered.event; + private readonly _onViewsDeregistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>(); + readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsDeregistered.event; - private _viewContainer: ViewContainer[] = []; + private _viewContainers: ViewContainer[] = []; private _views: Map = new Map(); - registerViews(viewDescriptors: IViewDescriptor[]): void { + registerViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): void { if (viewDescriptors.length) { + let views = this._views.get(viewContainer); + if (!views) { + views = []; + this._views.set(viewContainer, views); + this._viewContainers.push(viewContainer); + } for (const viewDescriptor of viewDescriptors) { - let views = this._views.get(viewDescriptor.container); - if (!views) { - views = []; - this._views.set(viewDescriptor.container, views); - this._viewContainer.push(viewDescriptor.container); - } if (views.some(v => v.id === viewDescriptor.id)) { - throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the container '{1}'", viewDescriptor.id, viewDescriptor.container.id)); + throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the container '{1}'", viewDescriptor.id, viewContainer.id)); } views.push(viewDescriptor); } - this._onViewsRegistered.fire(viewDescriptors); + this._onViewsRegistered.fire({ views: viewDescriptors, viewContainer }); } } - deregisterViews(ids: string[], container: ViewContainer): void { - const views = this._views.get(container); + deregisterViews(ids: string[], viewContainer: ViewContainer): void { + const views = this._views.get(viewContainer); if (!views) { return; @@ -184,12 +182,12 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry if (viewsToDeregister.length) { const remaningViews = views.filter(view => ids.indexOf(view.id) === -1); if (remaningViews.length) { - this._views.set(container, remaningViews); + this._views.set(viewContainer, remaningViews); } else { - this._views.delete(container); - this._viewContainer.splice(this._viewContainer.indexOf(container), 1); + this._views.delete(viewContainer); + this._viewContainers.splice(this._viewContainers.indexOf(viewContainer), 1); } - this._onViewsDeregistered.fire(viewsToDeregister); + this._onViewsDeregistered.fire({ views: viewsToDeregister, viewContainer }); } } @@ -199,7 +197,7 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry } getView(id: string): IViewDescriptor | null { - for (const viewContainer of this._viewContainer) { + for (const viewContainer of this._viewContainers) { const viewDescriptor = (this._views.get(viewContainer) || []).filter(v => v.id === id)[0]; if (viewDescriptor) { return viewDescriptor; @@ -208,10 +206,14 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry return null; } - getAllViews(): IViewDescriptor[] { - const allViews: IViewDescriptor[] = []; - this._views.forEach(views => allViews.push(...views)); - return allViews; + getViewContainer(viewId: string): ViewContainer | null { + for (const viewContainer of this._viewContainers) { + const viewDescriptor = (this._views.get(viewContainer) || []).filter(v => v.id === viewId)[0]; + if (viewDescriptor) { + return viewContainer; + } + } + return null; } }; @@ -297,7 +299,7 @@ export interface IRevealOptions { export interface ITreeViewDescriptor extends IViewDescriptor { readonly treeView: ITreeView; - readonly extensionId: ExtensionIdentifier; + } export type TreeViewItemHandleArg = { diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 2b19d7a96b0..7eac92e5ffc 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -112,11 +112,11 @@ Registry.as(PanelExtensions.Panels).registerPanel(new PanelDescri Registry.as(PanelExtensions.Panels).setDefaultPanelId(REPL_ID); // Register default debug views -ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, container: VIEW_CONTAINER, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' } }]); -ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, container: VIEW_CONTAINER, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' } }]); -ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, container: VIEW_CONTAINER, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' } }]); -ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, container: VIEW_CONTAINER, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }]); -ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctor: LoadedScriptsView, order: 35, weight: 5, container: VIEW_CONTAINER, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }]); +ViewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctor: VariablesView, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctor: WatchExpressionsView, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctor: CallStackView, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctor: BreakpointsView, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }], VIEW_CONTAINER); +ViewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctor: LoadedScriptsView, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }], VIEW_CONTAINER); // register action to open viewlet const registry = Registry.as(WorkbenchActionRegistryExtensions.WorkbenchActions); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts index d3c66db79c2..b90011dc02d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsViewlet.ts @@ -108,7 +108,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio viewDescriptors.push(...this.createExtensionsViewDescriptorsForServer(this.extensionManagementServerService.remoteExtensionManagementServer)); } - ViewsRegistry.registerViews(viewDescriptors); + ViewsRegistry.registerViews(viewDescriptors, VIEW_CONTAINER); } // View used for any kind of searching @@ -117,7 +117,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: ExtensionsListView, when: ContextKeyExpr.and(ContextKeyExpr.has('searchExtensions'), ContextKeyExpr.not('searchInstalledExtensions'), ContextKeyExpr.not('searchBuiltInExtensions'), ContextKeyExpr.not('recommendedExtensions'), ContextKeyExpr.not('groupByServersContext')), weight: 100 @@ -131,7 +130,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: EnabledExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 40, @@ -147,7 +145,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: DisabledExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('hasInstalledExtensions')), weight: 10, @@ -164,7 +161,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: ExtensionsListView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.not('hasInstalledExtensions')), weight: 60, @@ -176,7 +172,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return [{ id: `server.extensionsList.${server.authority}`, name: server.label, - container: VIEW_CONTAINER, ctor: GroupByServerExtensionsView, when: ContextKeyExpr.has('groupByServersContext'), weight: 100 @@ -191,7 +186,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: DefaultRecommendedExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.not('searchExtensions'), ContextKeyExpr.has('defaultRecommendedExtensions')), weight: 40, @@ -207,7 +201,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: RecommendedExtensionsView, when: ContextKeyExpr.has('recommendedExtensions'), weight: 50, @@ -223,7 +216,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: WorkspaceRecommendedExtensionsView, when: ContextKeyExpr.and(ContextKeyExpr.has('recommendedExtensions'), ContextKeyExpr.has('nonEmptyWorkspace')), weight: 50, @@ -237,7 +229,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: BuiltInExtensionsView, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, @@ -250,7 +241,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: BuiltInThemesExtensionsView, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, @@ -263,7 +253,6 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio return { id, name: viewIdNameMappings[id], - container: VIEW_CONTAINER, ctor: BuiltInBasicsExtensionsView, when: ContextKeyExpr.has('searchBuiltInExtensions'), weight: 100, diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index 7320d44ec90..074b8d98330 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -88,7 +88,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor } if (viewDescriptorsToRegister.length) { - ViewsRegistry.registerViews(viewDescriptorsToRegister); + ViewsRegistry.registerViews(viewDescriptorsToRegister, VIEW_CONTAINER); } if (viewDescriptorsToDeregister.length) { ViewsRegistry.deregisterViews(viewDescriptorsToDeregister, VIEW_CONTAINER); @@ -99,7 +99,6 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: OpenEditorsView.ID, name: OpenEditorsView.NAME, - container: VIEW_CONTAINER, ctor: OpenEditorsView, order: 0, when: OpenEditorsVisibleCondition, @@ -115,7 +114,6 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: EmptyView.ID, name: EmptyView.NAME, - container: VIEW_CONTAINER, ctor: EmptyView, order: 1, canToggleVisibility: false @@ -126,7 +124,6 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor return { id: ExplorerView.ID, name: localize('folders', "Folders"), - container: VIEW_CONTAINER, ctor: ExplorerView, order: 1, canToggleVisibility: false diff --git a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts index b69fc332dbd..4b85e7261a2 100644 --- a/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts +++ b/src/vs/workbench/parts/outline/electron-browser/outline.contribution.ts @@ -15,7 +15,6 @@ const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), ctor: OutlinePanel, - container: VIEW_CONTAINER, canToggleVisibility: true, hideByDefault: false, collapsed: true, @@ -24,7 +23,7 @@ const _outlineDesc = { focusCommand: { id: 'outline.focus' } }; -ViewsRegistry.registerViews([_outlineDesc]); +ViewsRegistry.registerViews([_outlineDesc], VIEW_CONTAINER); Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ 'id': 'outline', diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 129c96dfc90..31a9ce1b677 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -64,11 +64,10 @@ suite('ContributableViewsModel', () => { const viewDescriptor: IViewDescriptor = { id: 'view1', ctor: null, - container, name: 'Test View 1' }; - ViewsRegistry.registerViews([viewDescriptor]); + ViewsRegistry.registerViews([viewDescriptor], container); assert.equal(model.visibleViewDescriptors.length, 1); assert.equal(seq.elements.length, 1); @@ -91,12 +90,11 @@ suite('ContributableViewsModel', () => { const viewDescriptor: IViewDescriptor = { id: 'view1', ctor: null, - container, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; - ViewsRegistry.registerViews([viewDescriptor]); + ViewsRegistry.registerViews([viewDescriptor], container); assert.equal(model.visibleViewDescriptors.length, 0, 'view should not appear since context isnt in'); assert.equal(seq.elements.length, 0); @@ -130,10 +128,10 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, container, name: 'Test View 1' }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, container, name: 'Test View 2', when: ContextKeyExpr.equals('showview2', true) }; + const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1' }; + const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2', when: ContextKeyExpr.equals('showview2', true) }; - ViewsRegistry.registerViews([view1, view2]); + ViewsRegistry.registerViews([view1, view2], container); assert.deepEqual(model.visibleViewDescriptors, [view1], 'only view1 should be visible'); assert.deepEqual(seq.elements, [view1], 'only view1 should be visible'); @@ -153,10 +151,10 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, container, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, container, name: 'Test View 2' }; + const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; + const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2' }; - ViewsRegistry.registerViews([view1, view2]); + ViewsRegistry.registerViews([view1, view2], container); assert.deepEqual(model.visibleViewDescriptors, [view2], 'only view2 should be visible'); assert.deepEqual(seq.elements, [view2], 'only view2 should be visible'); @@ -176,11 +174,11 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, container, name: 'Test View 1', canToggleVisibility: true }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, container, name: 'Test View 2', canToggleVisibility: true }; - const view3: IViewDescriptor = { id: 'view3', ctor: null, container, name: 'Test View 3', canToggleVisibility: true }; + const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1', canToggleVisibility: true }; + const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2', canToggleVisibility: true }; + const view3: IViewDescriptor = { id: 'view3', ctor: null, name: 'Test View 3', canToggleVisibility: true }; - ViewsRegistry.registerViews([view1, view2, view3]); + ViewsRegistry.registerViews([view1, view2, view3], container); assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3]); assert.deepEqual(seq.elements, [view1, view2, view3]); @@ -221,11 +219,11 @@ suite('ContributableViewsModel', () => { const model = new ContributableViewsModel(container, viewsService); const seq = new ViewDescriptorSequence(model); - const view1: IViewDescriptor = { id: 'view1', ctor: null, container, name: 'Test View 1' }; - const view2: IViewDescriptor = { id: 'view2', ctor: null, container, name: 'Test View 2' }; - const view3: IViewDescriptor = { id: 'view3', ctor: null, container, name: 'Test View 3' }; + const view1: IViewDescriptor = { id: 'view1', ctor: null, name: 'Test View 1' }; + const view2: IViewDescriptor = { id: 'view2', ctor: null, name: 'Test View 2' }; + const view3: IViewDescriptor = { id: 'view3', ctor: null, name: 'Test View 3' }; - ViewsRegistry.registerViews([view1, view2, view3]); + ViewsRegistry.registerViews([view1, view2, view3], container); assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'model views should be OK'); assert.deepEqual(seq.elements, [view1, view2, view3], 'sql views should be OK'); From b323616e367e0e806189f46d07bf9740b25c909f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 16:35:16 +0100 Subject: [PATCH 0396/1066] #66653 Implement moving view descriptors --- .../browser/parts/views/customView.ts | 5 ++ src/vs/workbench/browser/parts/views/views.ts | 54 +++++++++++++++---- src/vs/workbench/common/views.ts | 18 +++++++ 3 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index daebf0af817..d0c1951d8bd 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -235,6 +235,11 @@ export class CustomTreeView extends Disposable implements ITreeView { this.markdownResult.dispose(); } })); + this._register(ViewsRegistry.onDidChangeContainer(({ views, from, to }) => { + if (from === this.viewContainer && views.some(v => v.id === this.id)) { + this.viewContainer = to; + } + })); this.create(); } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index c39900cfe01..646f20e594d 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -11,7 +11,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; -import { sortedDiff, firstIndex, move } from 'vs/base/common/arrays'; +import { sortedDiff, firstIndex, move, isNonEmptyArray } from 'vs/base/common/arrays'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions'; @@ -22,13 +22,20 @@ import { values } from 'vs/base/common/map'; import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { toggleClass, addClass } from 'vs/base/browser/dom'; -function filterViewEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event { +function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event { return Event.chain(event) .map(({ views, viewContainer }) => viewContainer === container ? views : []) .filter(views => views.length > 0) .event; } +function filterViewMoveEvent(container: ViewContainer, event: Event<{ from: ViewContainer, to: ViewContainer, views: IViewDescriptor[] }>): Event<{ added?: IViewDescriptor[], removed?: IViewDescriptor[] }> { + return Event.chain(event) + .map(({ views, from, to }) => from === container ? { removed: views } : to === container ? { added: views } : {}) + .filter(({ added, removed }) => isNonEmptyArray(added) || isNonEmptyArray(removed)) + .event; +} + class CounterSet implements IReadableSet { private map = new Map(); @@ -89,10 +96,20 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(); - const onRelevantViewsRegistered = filterViewEvent(container, ViewsRegistry.onViewsRegistered); + const onRelevantViewsRegistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsRegistered); this._register(onRelevantViewsRegistered(this.onViewsRegistered, this)); - const onRelevantViewsDeregistered = filterViewEvent(container, ViewsRegistry.onViewsDeregistered); + const onRelevantViewsMoved = filterViewMoveEvent(container, ViewsRegistry.onDidChangeContainer); + this._register(onRelevantViewsMoved(({ added, removed }) => { + if (isNonEmptyArray(added)) { + this.onViewsRegistered(added); + } + if (isNonEmptyArray(removed)) { + this.onViewsDeregistered(removed); + } + })); + + const onRelevantViewsDeregistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsDeregistered); this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this)); const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys)); @@ -517,7 +534,7 @@ export class ViewsService extends Disposable implements IViewsService { _serviceBrand: any; - private readonly viewDescriptorCollections: Map; + private readonly viewDisposable: Map; private readonly activeViewContextKeys: Map>; constructor( @@ -526,7 +543,7 @@ export class ViewsService extends Disposable implements IViewsService { ) { super(); - this.viewDescriptorCollections = new Map(); + this.viewDisposable = new Map(); this.activeViewContextKeys = new Map>(); const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); @@ -535,6 +552,12 @@ export class ViewsService extends Disposable implements IViewsService { this.onDidRegisterViewContainer(viewContainer); }); this._register(ViewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views))); + this._register(ViewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views))); + this._register(ViewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); })); + this._register(toDisposable(() => { + this.viewDisposable.forEach(disposable => disposable.dispose()); + this.viewDisposable.clear(); + })); this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer))); } @@ -576,6 +599,7 @@ export class ViewsService extends Disposable implements IViewsService { private onDidRegisterViews(container: ViewContainer, views: IViewDescriptor[]): void { const viewlet = this.viewletService.getViewlet(container.id); for (const viewDescriptor of views) { + const disposables: IDisposable[] = []; const command: ICommandAction = { id: viewDescriptor.focusCommand ? viewDescriptor.focusCommand.id : `${viewDescriptor.id}.focus`, title: { original: `Focus on ${viewDescriptor.name} View`, value: localize('focus view', "Focus on {0} View", viewDescriptor.name) }, @@ -583,12 +607,12 @@ export class ViewsService extends Disposable implements IViewsService { }; const when = ContextKeyExpr.has(`${viewDescriptor.id}.active`); - CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true).then(() => null)); + disposables.push(CommandsRegistry.registerCommand(command.id, () => this.openView(viewDescriptor.id, true).then(() => null))); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + disposables.push(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command, when - }); + })); if (viewDescriptor.focusCommand && viewDescriptor.focusCommand.keybindings) { KeybindingsRegistry.registerKeybindingRule({ @@ -602,6 +626,18 @@ export class ViewsService extends Disposable implements IViewsService { win: viewDescriptor.focusCommand.keybindings.win }); } + + this.viewDisposable.set(viewDescriptor, toDisposable(() => dispose(disposables))); + } + } + + private onDidDeregisterViews(views: IViewDescriptor[]): void { + for (const view of views) { + const disposable = this.viewDisposable.get(view); + if (disposable) { + disposable.dispose(); + this.viewDisposable.delete(view); + } } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index e4f720d6ab1..05de63f7980 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -130,10 +130,14 @@ export interface IViewsRegistry { readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }>; + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>; + registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; deregisterViews(ids: string[], viewContainer: ViewContainer): void; + moveViews(ids: string[], viewContainer: ViewContainer): void; + getViews(viewContainer: ViewContainer): IViewDescriptor[]; getView(id: string): IViewDescriptor | null; @@ -149,6 +153,9 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry private readonly _onViewsDeregistered: Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = new Emitter<{ views: IViewDescriptor[], viewContainer: ViewContainer }>(); readonly onViewsDeregistered: Event<{ views: IViewDescriptor[], viewContainer: ViewContainer }> = this._onViewsDeregistered.event; + private readonly _onDidChangeContainer: Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = new Emitter<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }>(); + readonly onDidChangeContainer: Event<{ views: IViewDescriptor[], from: ViewContainer, to: ViewContainer }> = this._onDidChangeContainer.event; + private _viewContainers: ViewContainer[] = []; private _views: Map = new Map(); @@ -192,6 +199,17 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry } + moveViews(ids: string[], viewContainer: ViewContainer): void { + this._views.forEach((views, container) => { + if (container !== viewContainer) { + const movedViews = views.filter(view => ids.indexOf(view.id) !== -1); + if (movedViews.length) { + this._onDidChangeContainer.fire({ views: movedViews, from: container, to: viewContainer }); + } + } + }); + } + getViews(loc: ViewContainer): IViewDescriptor[] { return this._views.get(loc) || []; } From f139602e4e4a8e55284a6a3dc746e15789f27e1a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 18:08:26 +0100 Subject: [PATCH 0397/1066] Fix #66653 --- .../api/browser/viewsExtensionPoint.ts | 109 ++++++++++----- src/vs/workbench/browser/parts/views/views.ts | 29 +++- .../browser/parts/views/viewsViewlet.ts | 8 +- src/vs/workbench/common/views.ts | 127 +++++++++++------- .../files/electron-browser/explorerViewlet.ts | 6 +- .../test/browser/parts/views/views.test.ts | 12 +- 6 files changed, 199 insertions(+), 92 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 2a800bf880f..6ab333bf1e4 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -8,7 +8,7 @@ import { forEach } from 'vs/base/common/collections'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import * as resources from 'vs/base/common/resources'; import { ExtensionMessageCollector, ExtensionsRegistry, IExtensionPoint, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ViewContainer, ViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID } from 'vs/workbench/common/views'; +import { ViewContainer, ViewsRegistry, ITreeViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, TEST_VIEW_CONTAINER_ID, IViewDescriptor } from 'vs/workbench/common/views'; import { CustomTreeViewPanel, CustomTreeView } from 'vs/workbench/browser/parts/views/customView'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { coalesce, } from 'vs/base/common/arrays'; @@ -138,12 +138,14 @@ const viewsContribution: IJSONSchema = { export interface ICustomViewDescriptor extends ITreeViewDescriptor { readonly extensionId: ExtensionIdentifier; + readonly originalContainerId: string; } type ViewContainerExtensionPointType = { [loc: string]: IUserFriendlyViewsContainerDescriptor[] }; const viewsContainersExtensionPoint: IExtensionPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'viewsContainers', - jsonSchema: viewsContainersContribution + jsonSchema: viewsContainersContribution, + isDynamic: true }); type ViewExtensionPointType = { [loc: string]: IUserFriendlyViewDescriptor[] }; @@ -169,31 +171,54 @@ class ViewsExtensionHandler implements IWorkbenchContribution { private handleAndRegisterCustomViewContainers() { this.registerTestViewContainer(); - - let order = TEST_VIEW_CONTAINER_ORDER + 1; - viewsContainersExtensionPoint.setHandler((extensions) => { - for (let extension of extensions) { - const { value, collector } = extension; - forEach(value, entry => { - if (!this.isValidViewsContainer(entry.value, collector)) { - return; - } - switch (entry.key) { - case 'activitybar': - order = this.registerCustomViewContainers(entry.value, extension.description, order); - break; - } - }); + viewsContainersExtensionPoint.setHandler((extensions, { added, removed }) => { + if (removed.length) { + this.removeCustomViewContainers(removed); + } + if (added.length) { + this.addCustomViewContainers(added, this.viewContainersRegistry.all); } }); } + private addCustomViewContainers(extensionPoints: IExtensionPointUser[], existingViewContainers: ViewContainer[]): void { + const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + let order = TEST_VIEW_CONTAINER_ORDER + viewContainersRegistry.all.filter(v => !!v.extensionId).length + 1; + for (let { value, collector, description } of extensionPoints) { + forEach(value, entry => { + if (!this.isValidViewsContainer(entry.value, collector)) { + return; + } + switch (entry.key) { + case 'activitybar': + order = this.registerCustomViewContainers(entry.value, description, order, existingViewContainers); + break; + } + }); + } + } + + private removeCustomViewContainers(extensionPoints: IExtensionPointUser[]): void { + const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); + const removedExtensions: Set = extensionPoints.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); + for (const viewContainer of viewContainersRegistry.all) { + if (viewContainer.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(viewContainer.extensionId))) { + // move only those views that do not belong to the removed extension + const views = ViewsRegistry.getViews(viewContainer).filter((view: ICustomViewDescriptor) => !removedExtensions.has(ExtensionIdentifier.toKey(view.extensionId))); + if (views.length) { + ViewsRegistry.moveViews(views, this.getDefaultViewContainer()); + } + this.deregisterCustomViewContainer(viewContainer); + } + } + } + private registerTestViewContainer(): void { const title = localize('test', "Test"); const cssClass = `extensionViewlet-test`; const icon = URI.parse(require.toUrl('./media/test.svg')); - this.registerCustomViewlet(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined); + this.registerCustomViewContainer(TEST_VIEW_CONTAINER_ID, title, icon, TEST_VIEW_CONTAINER_ORDER, cssClass, undefined); } private isValidViewsContainer(viewsContainersDescriptors: IUserFriendlyViewsContainerDescriptor[], collector: ExtensionMessageCollector): boolean { @@ -224,22 +249,35 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return true; } - private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number): number { + private registerCustomViewContainers(containers: IUserFriendlyViewsContainerDescriptor[], extension: IExtensionDescription, order: number, existingViewContainers: ViewContainer[]): number { containers.forEach(descriptor => { const cssClass = `extensionViewlet-${descriptor.id}`; const icon = resources.joinPath(extension.extensionLocation, descriptor.icon); - this.registerCustomViewlet(`workbench.view.extension.${descriptor.id}`, descriptor.title, icon, order++, cssClass, extension.identifier); + const id = `workbench.view.extension.${descriptor.id}`; + const viewContainer = this.registerCustomViewContainer(id, descriptor.title, icon, order++, cssClass, extension.identifier); + + // Move those views that belongs to this container + if (existingViewContainers.length) { + const viewsToMove: IViewDescriptor[] = []; + for (const existingViewContainer of existingViewContainers) { + if (viewContainer !== existingViewContainer) { + viewsToMove.push(...ViewsRegistry.getViews(existingViewContainer).filter((view: ICustomViewDescriptor) => view.originalContainerId === descriptor.id)); + } + } + if (viewsToMove.length) { + ViewsRegistry.moveViews(viewsToMove, viewContainer); + } + } }); return order; } - private registerCustomViewlet(id: string, title: string, icon: URI, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): void { - const viewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); - const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + private registerCustomViewContainer(id: string, title: string, icon: URI, order: number, cssClass: string, extensionId: ExtensionIdentifier | undefined): ViewContainer { + let viewContainer = this.viewContainersRegistry.get(id); - if (!viewletRegistry.getViewlet(id)) { + if (!viewContainer) { - viewContainersRegistry.registerViewContainer(id, extensionId); + viewContainer = this.viewContainersRegistry.registerViewContainer(id, extensionId); // Register as viewlet class CustomViewlet extends ViewContainerViewlet { @@ -267,7 +305,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { icon ); - viewletRegistry.registerViewlet(viewletDescriptor); + Registry.as(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); // Register Action to Open Viewlet class OpenCustomViewletAction extends ShowViewletAction { @@ -292,6 +330,12 @@ class ViewsExtensionHandler implements IWorkbenchContribution { createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`); } + return viewContainer; + } + + private deregisterCustomViewContainer(viewContainer: ViewContainer): void { + this.viewContainersRegistry.deregisterViewContainer(viewContainer); + Registry.as(ViewletExtensions.Viewlets).deregisterViewlet(viewContainer.id); } private handleAndRegisterCustomViews() { @@ -317,7 +361,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { let container = this.getViewContainer(entry.key); if (!container) { collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key)); - container = this.viewContainersRegistry.get(EXPLORER); + container = this.getDefaultViewContainer(); } const registeredViews = ViewsRegistry.getViews(container); const viewIds: string[] = []; @@ -332,7 +376,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return null; } - const viewDescriptor = { + const viewDescriptor = { id: item.id, name: item.name, ctor: CustomTreeViewPanel, @@ -341,7 +385,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution { 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 + extensionId: extension.description.identifier, + originalContainerId: entry.key }; viewIds.push(viewDescriptor.id); @@ -352,12 +397,16 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } + private getDefaultViewContainer(): ViewContainer { + return this.viewContainersRegistry.get(EXPLORER); + } + private removeViews(extensions: IExtensionPointUser[]): void { const removedExtensions: Set = extensions.reduce((result, e) => { result.add(ExtensionIdentifier.toKey(e.description.identifier)); return result; }, new Set()); for (const viewContainer of this.viewContainersRegistry.all) { const removedViews = ViewsRegistry.getViews(viewContainer).filter((v: ICustomViewDescriptor) => v.extensionId && removedExtensions.has(ExtensionIdentifier.toKey(v.extensionId))); if (removedViews.length) { - ViewsRegistry.deregisterViews(removedViews.map(v => v.id), viewContainer); + ViewsRegistry.deregisterViews(removedViews, viewContainer); } } } diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 646f20e594d..b779f81a8d5 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/views'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle'; import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView, IViewDescriptorCollection } from 'vs/workbench/common/views'; import { Registry } from 'vs/platform/registry/common/platform'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -534,6 +534,7 @@ export class ViewsService extends Disposable implements IViewsService { _serviceBrand: any; + private readonly viewDescriptorCollections: Map; private readonly viewDisposable: Map; private readonly activeViewContextKeys: Map>; @@ -543,6 +544,7 @@ export class ViewsService extends Disposable implements IViewsService { ) { super(); + this.viewDescriptorCollections = new Map(); this.viewDisposable = new Map(); this.activeViewContextKeys = new Map>(); @@ -559,10 +561,16 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDisposable.clear(); })); this._register(viewContainersRegistry.onDidRegister(viewContainer => this.onDidRegisterViewContainer(viewContainer))); + this._register(viewContainersRegistry.onDidDeregister(viewContainer => this.onDidDeregisterViewContainer(viewContainer))); + this._register(toDisposable(() => { + this.viewDescriptorCollections.forEach(({ disposable }) => disposable.dispose()); + this.viewDescriptorCollections.clear(); + })); } - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection { - return this.viewDescriptorCollections.get(container)!; + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null { + const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(container); + return viewDescriptorCollectionItem ? viewDescriptorCollectionItem.viewDescriptorCollection : null; } openView(id: string, focus: boolean): Promise { @@ -583,12 +591,21 @@ export class ViewsService extends Disposable implements IViewsService { } private onDidRegisterViewContainer(viewContainer: ViewContainer): void { - const viewDescriptorCollection = this._register(new ViewDescriptorCollection(viewContainer, this.contextKeyService)); + const viewDescriptorCollection = new ViewDescriptorCollection(viewContainer, this.contextKeyService); + const disposables: IDisposable[] = [viewDescriptorCollection]; this.onDidChangeActiveViews({ added: viewDescriptorCollection.activeViewDescriptors, removed: [] }); - this._register(viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed))); + viewDescriptorCollection.onDidChangeActiveViews(changed => this.onDidChangeActiveViews(changed), this, disposables); - this.viewDescriptorCollections.set(viewContainer, viewDescriptorCollection); + this.viewDescriptorCollections.set(viewContainer, { viewDescriptorCollection, disposable: toDisposable(() => dispose(disposables)) }); + } + + private onDidDeregisterViewContainer(viewContainer: ViewContainer): void { + const viewDescriptorCollectionItem = this.viewDescriptorCollections.get(viewContainer); + if (viewDescriptorCollectionItem) { + viewDescriptorCollectionItem.disposable.dispose(); + this.viewDescriptorCollections.delete(viewContainer); + } } private onDidChangeActiveViews({ added, removed }: { added: IViewDescriptor[], removed: IViewDescriptor[] }): void { diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 767e16d82df..a3c3be3ee57 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -214,7 +214,13 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView this.addPanels(panelsToAdd); this.restoreViewSizes(); - return panelsToAdd.map(({ panel }) => panel); + + const panels: ViewletPanel[] = []; + for (const { panel } of panelsToAdd) { + panel.setVisible(this.isVisible()); + panels.push(panel); + } + return panels; } private onDidRemoveViews(removed: IViewDescriptorRef[]): void { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 05de63f7980..15fa9d4b93e 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -13,7 +13,7 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { values } from 'vs/base/common/map'; +import { values, keys } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IAction } from 'vs/base/common/actions'; @@ -32,6 +32,11 @@ export interface IViewContainersRegistry { */ readonly onDidRegister: Event; + /** + * An event that is triggerred when a view container is deregistered. + */ + readonly onDidDeregister: Event; + /** * All registered view containers */ @@ -47,6 +52,12 @@ export interface IViewContainersRegistry { */ registerViewContainer(id: string, extensionId?: ExtensionIdentifier): ViewContainer; + /** + * Deregisters the given view container + * No op if the view container is not registered + */ + deregisterViewContainer(viewContainer: ViewContainer): void; + /** * Returns the view container with given id. * @@ -64,6 +75,9 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry { private readonly _onDidRegister = new Emitter(); readonly onDidRegister: Event = this._onDidRegister.event; + private readonly _onDidDeregister = new Emitter(); + readonly onDidDeregister: Event = this._onDidDeregister.event; + private viewContainers: Map = new Map(); get all(): ViewContainer[] { @@ -86,6 +100,14 @@ class ViewContainersRegistryImpl implements IViewContainersRegistry { return viewContainer; } + deregisterViewContainer(viewContainer: ViewContainer): void { + const existing = this.viewContainers.get(viewContainer.id); + if (existing) { + this.viewContainers.delete(viewContainer.id); + this._onDidDeregister.fire(viewContainer); + } + } + get(id: string): ViewContainer | undefined { return this.viewContainers.get(id); } @@ -118,7 +140,7 @@ export interface IViewDescriptor { readonly focusCommand?: { id: string, keybindings?: IKeybindings }; } -export interface IViewDescriptorCollection { +export interface IViewDescriptorCollection extends IDisposable { readonly onDidChangeActiveViews: Event<{ added: IViewDescriptor[], removed: IViewDescriptor[] }>; readonly activeViewDescriptors: IViewDescriptor[]; readonly allViewDescriptors: IViewDescriptor[]; @@ -134,9 +156,9 @@ export interface IViewsRegistry { registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; - deregisterViews(ids: string[], viewContainer: ViewContainer): void; + deregisterViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; - moveViews(ids: string[], viewContainer: ViewContainer): void; + moveViews(views: IViewDescriptor[], viewContainer: ViewContainer): void; getViews(viewContainer: ViewContainer): IViewDescriptor[]; @@ -159,52 +181,25 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry private _viewContainers: ViewContainer[] = []; private _views: Map = new Map(); - registerViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): void { - if (viewDescriptors.length) { - let views = this._views.get(viewContainer); - if (!views) { - views = []; - this._views.set(viewContainer, views); - this._viewContainers.push(viewContainer); - } - for (const viewDescriptor of viewDescriptors) { - if (views.some(v => v.id === viewDescriptor.id)) { - throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the container '{1}'", viewDescriptor.id, viewContainer.id)); - } - views.push(viewDescriptor); - } - this._onViewsRegistered.fire({ views: viewDescriptors, viewContainer }); + registerViews(views: IViewDescriptor[], viewContainer: ViewContainer): void { + this.addViews(views, viewContainer); + this._onViewsRegistered.fire({ views: views, viewContainer }); + } + + deregisterViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): void { + const views = this.removeViews(viewDescriptors, viewContainer); + if (views.length) { + this._onViewsDeregistered.fire({ views, viewContainer }); } } - deregisterViews(ids: string[], viewContainer: ViewContainer): void { - const views = this._views.get(viewContainer); - - if (!views) { - return; - } - - const viewsToDeregister = views.filter(view => ids.indexOf(view.id) !== -1); - - if (viewsToDeregister.length) { - const remaningViews = views.filter(view => ids.indexOf(view.id) === -1); - if (remaningViews.length) { - this._views.set(viewContainer, remaningViews); - } else { - this._views.delete(viewContainer); - this._viewContainers.splice(this._viewContainers.indexOf(viewContainer), 1); - } - this._onViewsDeregistered.fire({ views: viewsToDeregister, viewContainer }); - } - - } - - moveViews(ids: string[], viewContainer: ViewContainer): void { - this._views.forEach((views, container) => { + moveViews(viewsToMove: IViewDescriptor[], viewContainer: ViewContainer): void { + keys(this._views).forEach(container => { if (container !== viewContainer) { - const movedViews = views.filter(view => ids.indexOf(view.id) !== -1); - if (movedViews.length) { - this._onDidChangeContainer.fire({ views: movedViews, from: container, to: viewContainer }); + const views = this.removeViews(viewsToMove, container); + if (views.length) { + this.addViews(views, viewContainer); + this._onDidChangeContainer.fire({ views, from: container, to: viewContainer }); } } }); @@ -233,6 +228,46 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry } return null; } + + private addViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): void { + let views = this._views.get(viewContainer); + if (!views) { + views = []; + this._views.set(viewContainer, views); + this._viewContainers.push(viewContainer); + } + for (const viewDescriptor of viewDescriptors) { + if (views.some(v => v.id === viewDescriptor.id)) { + throw new Error(localize('duplicateId', "A view with id '{0}' is already registered in the container '{1}'", viewDescriptor.id, viewContainer.id)); + } + views.push(viewDescriptor); + } + } + + private removeViews(viewDescriptors: IViewDescriptor[], viewContainer: ViewContainer): IViewDescriptor[] { + const views = this._views.get(viewContainer); + if (!views) { + return []; + } + const viewsToDeregister: IViewDescriptor[] = []; + const remaningViews: IViewDescriptor[] = []; + for (const view of views) { + if (viewDescriptors.indexOf(view) === -1) { + remaningViews.push(view); + } else { + viewsToDeregister.push(view); + } + } + if (viewsToDeregister.length) { + if (remaningViews.length) { + this._views.set(viewContainer, remaningViews); + } else { + this._views.delete(viewContainer); + this._viewContainers.splice(this._viewContainers.indexOf(viewContainer), 1); + } + } + return viewsToDeregister; + } }; export interface IView { diff --git a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts index 074b8d98330..7c01ed22445 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerViewlet.ts @@ -59,7 +59,7 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor const viewDescriptors = ViewsRegistry.getViews(VIEW_CONTAINER); let viewDescriptorsToRegister: IViewDescriptor[] = []; - let viewDescriptorsToDeregister: string[] = []; + let viewDescriptorsToDeregister: IViewDescriptor[] = []; const openEditorsViewDescriptor = this.createOpenEditorsViewDescriptor(); const openEditorsViewDescriptorExists = viewDescriptors.some(v => v.id === openEditorsViewDescriptor.id); @@ -73,14 +73,14 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor } if (this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY || this.workspaceContextService.getWorkspace().folders.length === 0) { if (explorerViewDescriptorExists) { - viewDescriptorsToDeregister.push(explorerViewDescriptor.id); + viewDescriptorsToDeregister.push(explorerViewDescriptor); } if (!emptyViewDescriptorExists) { viewDescriptorsToRegister.push(emptyViewDescriptor); } } else { if (emptyViewDescriptorExists) { - viewDescriptorsToDeregister.push(emptyViewDescriptor.id); + viewDescriptorsToDeregister.push(emptyViewDescriptor); } if (!explorerViewDescriptorExists) { viewDescriptorsToRegister.push(explorerViewDescriptor); diff --git a/src/vs/workbench/test/browser/parts/views/views.test.ts b/src/vs/workbench/test/browser/parts/views/views.test.ts index 31a9ce1b677..11fd665875f 100644 --- a/src/vs/workbench/test/browser/parts/views/views.test.ts +++ b/src/vs/workbench/test/browser/parts/views/views.test.ts @@ -46,7 +46,7 @@ suite('ContributableViewsModel', () => { }); teardown(() => { - ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container).map(({ id }) => id), container); + ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container); }); test('empty model', function () { @@ -74,7 +74,7 @@ suite('ContributableViewsModel', () => { assert.deepEqual(model.visibleViewDescriptors[0], viewDescriptor); assert.deepEqual(seq.elements[0], viewDescriptor); - ViewsRegistry.deregisterViews(['view1'], container); + ViewsRegistry.deregisterViews([viewDescriptor], container); assert.equal(model.visibleViewDescriptors.length, 0); assert.equal(seq.elements.length, 0); @@ -114,7 +114,7 @@ suite('ContributableViewsModel', () => { assert.equal(model.visibleViewDescriptors.length, 0, 'view should disappear'); assert.equal(seq.elements.length, 0); - ViewsRegistry.deregisterViews(['view1'], container); + ViewsRegistry.deregisterViews([viewDescriptor], container); assert.equal(model.visibleViewDescriptors.length, 0, 'view should not be there anymore'); assert.equal(seq.elements.length, 0); @@ -144,7 +144,7 @@ suite('ContributableViewsModel', () => { assert.deepEqual(model.visibleViewDescriptors, [view1, view2], 'both views should be visible'); assert.deepEqual(seq.elements, [view1, view2], 'both views should be visible'); - ViewsRegistry.deregisterViews([view1.id, view2.id], container); + ViewsRegistry.deregisterViews([view1, view2], container); }); test('when contexts - multiple 2', async function () { @@ -167,7 +167,7 @@ suite('ContributableViewsModel', () => { assert.deepEqual(model.visibleViewDescriptors, [view1, view2], 'both views should be visible'); assert.deepEqual(seq.elements, [view1, view2], 'both views should be visible'); - ViewsRegistry.deregisterViews([view1.id, view2.id], container); + ViewsRegistry.deregisterViews([view1, view2], container); }); test('setVisible', () => { @@ -210,7 +210,7 @@ suite('ContributableViewsModel', () => { assert.deepEqual(model.visibleViewDescriptors, [view1, view2, view3], 'view2 should show'); assert.deepEqual(seq.elements, [view1, view2, view3]); - ViewsRegistry.deregisterViews([view1.id, view2.id, view3.id], container); + ViewsRegistry.deregisterViews([view1, view2, view3], container); assert.deepEqual(model.visibleViewDescriptors, []); assert.deepEqual(seq.elements, []); }); From 0bb40fbb0df6e4f2a2d2bcd14793b7392d39b01c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 18:32:07 +0100 Subject: [PATCH 0398/1066] #66735 Update on runtime extensions change --- .../extensions/electron-browser/extensionsActions.ts | 8 +++++++- .../parts/extensions/electron-browser/extensionsList.ts | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 13b4c5b8462..b26c648600a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -1074,6 +1074,7 @@ export class ReloadAction extends ExtensionAction { private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`; private throttler: Throttler; + private disposables: IDisposable[] = []; constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1085,7 +1086,7 @@ export class ReloadAction extends ExtensionAction { ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); this.throttler = new Throttler(); - + this.extensionService.onDidChangeExtensions(this.update, this, this.disposables); this.update(); } @@ -1178,6 +1179,11 @@ export class ReloadAction extends ExtensionAction { run(): Promise { return Promise.resolve(this.windowService.reloadWindow()); } + + dispose(): void { + dispose(this.disposables); + super.dispose(); + } } export class OpenExtensionsViewletAction extends ShowViewletAction { diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index fca55b71ed5..e2323ca0895 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -143,9 +143,10 @@ export class Renderer implements IPagedRenderer { removeClass(data.element, 'loading'); data.extensionDisposables = dispose(data.extensionDisposables); - const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; - this.extensionService.getExtensions().then(runningExtensions => { + const updateEnablement = async () => { + const runningExtensions = await this.extensionService.getExtensions(); + const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, extension.identifier))[0]; if (installed && installed.local) { const installedExtensionServer = this.extensionManagementServerService.getExtensionManagementServer(installed.local.location); const isSameExtensionRunning = runningExtensions.some(e => { @@ -162,7 +163,9 @@ export class Renderer implements IPagedRenderer { } else { removeClass(data.root, 'disabled'); } - }); + }; + updateEnablement(); + this.extensionService.onDidChangeExtensions(() => updateEnablement(), this, data.extensionDisposables); const onError = Event.once(domEvent(data.icon, 'error')); onError(() => data.icon.src = extension.iconUrlFallback, null, data.extensionDisposables); From f14576936da3bcb217e3287d4738467b1d691ae3 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 18:38:45 +0100 Subject: [PATCH 0399/1066] #66735 Fix tests --- .../extensions/test/electron-browser/extensionsActions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bb2e6771815..2a48e3dfcb9 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 @@ -84,7 +84,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); - instantiationService.stub(IExtensionService, { getExtensions: () => Promise.resolve([]) }); + instantiationService.stub(IExtensionService, { getExtensions: () => Promise.resolve([]), onDidChangeExtensions: new Emitter().event }); await (instantiationService.get(IExtensionEnablementService)).reset(); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); From 7cc405ad12b70f6366d47989fcf1f58972b3f1e6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 18 Jan 2019 18:44:04 +0100 Subject: [PATCH 0400/1066] #66653 Fix null checks --- src/tsconfig.strictNullChecks.json | 1 - .../parts/activitybar/activitybarPart.ts | 22 +++++++++++++------ src/vs/workbench/browser/parts/views/views.ts | 6 +++-- src/vs/workbench/common/views.ts | 2 +- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 114996cfc82..08bfbe946ed 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -513,7 +513,6 @@ "./vs/platform/workspaces/node/workspacesIpc.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", - "./vs/workbench/api/browser/viewsContainersExtensionPoint.ts", "./vs/workbench/api/node/extHostExtensionActivator.ts", "./vs/workbench/api/node/extHostSearch.fileIndex.ts", "./vs/workbench/api/node/extHostTypes.ts", diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 372cacb08fd..404646c23f5 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -124,8 +124,10 @@ export class ActivitybarPart extends Part { const viewContainer = this.getViewContainer(viewlet.id); if (viewContainer) { const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); - this.onDidChangeActiveViews(viewlet, viewDescriptors); - viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors)); + if (viewDescriptors) { + this.onDidChangeActiveViews(viewlet, viewDescriptors); + viewDescriptors.onDidChangeActiveViews(() => this.onDidChangeActiveViews(viewlet, viewDescriptors)); + } } } this.saveCachedViewlets(); @@ -145,9 +147,12 @@ export class ActivitybarPart extends Part { this.compositeBar.activateComposite(viewlet.getId()); const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId()); const viewContainer = this.getViewContainer(viewletDescriptor.id); - if (viewContainer && this.viewsService.getViewDescriptors(viewContainer).activeViewDescriptors.length === 0) { - // Update the composite bar by hiding - this.removeComposite(viewletDescriptor.id, true); + if (viewContainer) { + const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) { + // Update the composite bar by hiding + this.removeComposite(viewletDescriptor.id, true); + } } } @@ -396,8 +401,11 @@ export class ActivitybarPart extends Part { if (viewlet) { const views: { when: string }[] = []; if (viewContainer) { - for (const { when } of this.viewsService.getViewDescriptors(viewContainer).allViewDescriptors) { - views.push({ when: when ? when.serialize() : undefined }); + const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer); + if (viewDescriptors) { + for (const { when } of viewDescriptors.allViewDescriptors) { + views.push({ when: when ? when.serialize() : undefined }); + } } } state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible }); diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index b779f81a8d5..b1c99bc090a 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -243,8 +243,10 @@ export class ContributableViewsModel extends Disposable { super(); const viewDescriptorCollection = viewsService.getViewDescriptors(container); - this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors))); - this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors); + if (viewDescriptorCollection) { + this._register(viewDescriptorCollection.onDidChangeActiveViews(() => this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors))); + this.onDidChangeViewDescriptors(viewDescriptorCollection.activeViewDescriptors); + } } isVisible(id: string): boolean { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 15fa9d4b93e..f5effa730ab 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -289,7 +289,7 @@ export interface IViewsService { openView(id: string, focus?: boolean): Promise; - getViewDescriptors(container: ViewContainer): IViewDescriptorCollection; + getViewDescriptors(container: ViewContainer): IViewDescriptorCollection | null; } // Custom views From 9a3ef8c4b28ab583d967a7fc6e48efb741fde8ce Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 16 Jan 2019 09:59:06 -0800 Subject: [PATCH 0401/1066] Load html data using new API per Microsoft/vscode-html-languageservice#45 --- .../client/src/customData.ts | 29 ++++------ .../client/src/htmlMain.ts | 5 +- .../html-language-features/package.json | 8 +-- .../server/src/htmlServerMain.ts | 44 +++++++-------- .../server/src/modes/languageModes.ts | 7 ++- .../server/src/utils/tagDefinitions.ts | 54 ++----------------- 6 files changed, 37 insertions(+), 110 deletions(-) diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index 5e7c1c99c16..fb89f811769 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -8,21 +8,17 @@ import { workspace, WorkspaceFolder } from 'vscode'; interface ExperimentalConfig { experimental?: { - custom?: { - tags?: string[]; - attributes?: string[]; - } + customData?: string[]; }; } export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFolder[] | undefined) { - const tagPaths: string[] = []; - const attributePaths: string[] = []; + const dataPaths: string[] = []; + if (!workspaceFolders) { return { - tagPaths, - attributePaths + dataPaths }; } @@ -33,24 +29,17 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol if ( wfHtmlConfig && wfHtmlConfig.workspaceFolderValue && - wfHtmlConfig.workspaceFolderValue.experimental && - wfHtmlConfig.workspaceFolderValue.experimental.custom + wfHtmlConfig.workspaceFolderValue.experimental ) { - if (wfHtmlConfig.workspaceFolderValue.experimental.custom.tags) { - wfHtmlConfig.workspaceFolderValue.experimental.custom.tags.forEach(t => { - tagPaths.push(path.resolve(wf.uri.fsPath, t)); - }); - } - if (wfHtmlConfig.workspaceFolderValue.experimental.custom.attributes) { - wfHtmlConfig.workspaceFolderValue.experimental.custom.attributes.forEach(a => { - attributePaths.push(path.resolve(wf.uri.fsPath, a)); + if (wfHtmlConfig.workspaceFolderValue.experimental.customData) { + wfHtmlConfig.workspaceFolderValue.experimental.customData.forEach(t => { + dataPaths.push(path.resolve(wf.uri.fsPath, t)); }); } } }); return { - tagPaths, - attributePaths + dataPaths }; } diff --git a/extensions/html-language-features/client/src/htmlMain.ts b/extensions/html-language-features/client/src/htmlMain.ts index 0b4a33b776b..a95a8a7fc25 100644 --- a/extensions/html-language-features/client/src/htmlMain.ts +++ b/extensions/html-language-features/client/src/htmlMain.ts @@ -50,7 +50,7 @@ export function activate(context: ExtensionContext) { let documentSelector = ['html', 'handlebars', 'razor']; let embeddedLanguages = { css: true, javascript: true }; - let { tagPaths, attributePaths } = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); + let { dataPaths } = getCustomDataPathsInAllWorkspaces(workspace.workspaceFolders); // Options to control the language client let clientOptions: LanguageClientOptions = { @@ -60,8 +60,7 @@ export function activate(context: ExtensionContext) { }, initializationOptions: { embeddedLanguages, - tagPaths, - attributePaths + dataPaths } }; diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index ca3320124e5..812b6104c1b 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -31,13 +31,9 @@ "type": "object", "title": "HTML", "properties": { - "html.experimental.custom.tags": { + "html.experimental.customData": { "type": "array", - "description": "A list of JSON file paths that define custom tags. Only workspace folder setting will be read." - }, - "html.experimental.custom.attributes": { - "type": "array", - "description": "A list of JSON file paths that define custom attributes. Only workspace folder setting will be read." + "description": "A list of JSON file paths that define custom tags, properties and other HTML syntax constructs. Only workspace folder setting will be read." }, "html.format.enable": { "type": "boolean", diff --git a/extensions/html-language-features/server/src/htmlServerMain.ts b/extensions/html-language-features/server/src/htmlServerMain.ts index 1b32205c2e3..cec1d1da31e 100644 --- a/extensions/html-language-features/server/src/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/htmlServerMain.ts @@ -20,8 +20,8 @@ import uri from 'vscode-uri'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; import { getFoldingRanges } from './modes/htmlFolding'; -import { parseTagSet, parseAttributes } from './utils/tagDefinitions'; -import { ITagSet, IAttributeSet } from 'vscode-html-languageservice'; +import { parseHTMLData } from './utils/tagDefinitions'; +import { HTMLData } from 'vscode-html-languageservice'; namespace TagCloseRequest { export const type: RequestType = new RequestType('html/tag'); @@ -91,18 +91,24 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } } - const tagPaths: string[] = params.initializationOptions.tagPaths; - const attributePaths: string[] = params.initializationOptions.attributePaths; - const htmlTags: ITagSet = {}; - const htmlAttributes: IAttributeSet = {}; + const dataPaths: string[] = params.initializationOptions.dataPaths; - if (tagPaths) { - tagPaths.forEach(path => { + let allHtmlData: HTMLData = { + tags: [], + globalAttributes: [], + valueSetMap: {} + }; + + if (dataPaths) { + dataPaths.forEach(path => { try { if (fs.existsSync(path)) { - const tagSet = parseTagSet(fs.readFileSync(path, 'utf-8')); - for (let tag in tagSet) { - htmlTags[tag] = tagSet[tag]; + 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) { @@ -110,27 +116,13 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } }); } - if (htmlAttributes) { - attributePaths.forEach(path => { - try { - if (fs.existsSync(path)) { - const attributeSet = parseAttributes(fs.readFileSync(path, 'utf-8')); - for (let ga in attributeSet) { - htmlAttributes[ga] = attributeSet[ga]; - } - } - } catch (err) { - console.log(`Failed to load attributes from ${path}`); - } - }); - } const workspace = { get settings() { return globalSettings; }, get folders() { return workspaceFolders; } }; - languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, htmlTags, htmlAttributes); + languageModes = getLanguageModes(initializationOptions ? initializationOptions.embeddedLanguages : { css: true, javascript: true }, workspace, allHtmlData); 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 28b90a4cd0c..b398dc5b364 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, ITagSet, IAttributeSet } from 'vscode-html-languageservice'; +import { getLanguageService as getHTMLLanguageService, DocumentContext, HTMLData } from 'vscode-html-languageservice'; import { CompletionItem, Location, SignatureHelp, Definition, TextEdit, TextDocument, Diagnostic, DocumentLink, Range, Hover, DocumentHighlight, CompletionList, Position, FormattingOptions, SymbolInformation, FoldingRange @@ -65,11 +65,10 @@ export interface LanguageModeRange extends Range { attributeValue?: boolean; } -export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customTags?: ITagSet, customAttributes?: IAttributeSet): LanguageModes { +export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customData?: HTMLData): LanguageModes { const htmlLanguageService = getHTMLLanguageService({ - customTags, - customAttributes + customData }); 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 index 84bb3395c2b..110a2acc5d7 100644 --- a/extensions/html-language-features/server/src/utils/tagDefinitions.ts +++ b/extensions/html-language-features/server/src/utils/tagDefinitions.ts @@ -3,56 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITagSet, IAttributeSet, HTMLTagSpecification } from 'vscode-html-languageservice'; +import { HTMLData } from 'vscode-html-languageservice'; -interface Tag { - label: string; - description: string; - attributes: Attribute[]; -} -interface Attribute { - label: string; - description: string; -} -interface RawTagSet { - tags: Tag[]; -} -interface RawAttributeSet { - attributes: Attribute[]; -} - -export function parseTagSet(source: string): ITagSet { - const tagSet: ITagSet = {}; - - let rawTagSet: RawTagSet; - try { - rawTagSet = JSON.parse(source); - } catch (err) { - return {}; - } - - rawTagSet.tags.forEach(c => { - tagSet[c.label] = new HTMLTagSpecification(c.description, c.attributes.map(a => a.label)); - }); - - return tagSet; -} - -export function parseAttributes(source: string): IAttributeSet { - const attributeSet: IAttributeSet = {}; - - let rawAttributeSet: RawAttributeSet; - try { - rawAttributeSet = JSON.parse(source); - } catch (err) { - return {}; - } - - rawAttributeSet.attributes.forEach(ag => { - attributeSet[ag.label] = { - ...ag - }; - }); - - return attributeSet; +export function parseHTMLData(source: string): HTMLData { + return JSON.parse(source); } \ No newline at end of file From 02b7e03b7280920bb21bc159ed31f794def57b26 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Fri, 18 Jan 2019 11:16:20 -0800 Subject: [PATCH 0402/1066] Update service --- .../html-language-features/server/package.json | 4 ++-- .../server/src/modes/languageModes.ts | 6 ++---- .../html-language-features/server/yarn.lock | 16 ++++++++-------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 14eb36c4dd9..77ceb8edc5d 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.3", - "vscode-html-languageservice": "^2.1.11-next.2", + "vscode-css-languageservice": "^3.0.13-next.6", + "vscode-html-languageservice": "^2.1.11-next.3", "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/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index b398dc5b364..e0b073bdaef 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -66,10 +66,8 @@ export interface LanguageModeRange extends Range { } export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, customData?: HTMLData): LanguageModes { - - const htmlLanguageService = getHTMLLanguageService({ - customData - }); + const customDataCollections = customData ? [customData] : []; + const htmlLanguageService = getHTMLLanguageService({ customDataCollections }); let documentRegions = getLanguageModelCache(10, 60, document => getDocumentRegions(htmlLanguageService, document)); diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index 8bb4641bf0c..3bb00545094 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.3: - version "3.0.13-next.3" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.3.tgz#b9e6f253cace52fbb749d2fe194ae4b196f3543e" - integrity sha512-7+7JddZRt8zFRLbqygxJw+GHtbQ5o2YvgEFvi4ixvbUAX6KlY3Yw6CgUUbg9dmBla7h+GbtoDjwiLFV6Oy+SBQ== +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== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" -vscode-html-languageservice@^2.1.11-next.2: - version "2.1.11-next.2" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.2.tgz#0f0f840c2692481199b469e317da38ac2143b975" - integrity sha512-aFHtSgGxoxP8EeDDMk0VTAW6pns6ZlJRrwNQxQsFFuJ0PDgJdH5zVUfBOrY47EJHXdxuPSs1/PqZq1LsUY3x3w== +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== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 073aba19232c7f55b66e2ba2826b44171fd1b191 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Jan 2019 11:27:17 -0800 Subject: [PATCH 0403/1066] first cut proposed api for vscode.open, #66741 --- src/vs/vscode.proposed.d.ts | 11 +++++++++++ .../api/electron-browser/mainThreadWindow.ts | 15 ++++++++++++--- src/vs/workbench/api/node/extHost.api.impl.ts | 3 +++ src/vs/workbench/api/node/extHost.protocol.ts | 1 + src/vs/workbench/api/node/extHostWindow.ts | 5 +++++ 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f659d20660c..f95e97c3ced 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,6 +16,17 @@ declare module 'vscode' { + //#region Joh - vscode.open + + export namespace window { + /** + * + */ + export function open(uri: Uri): void; + } + + //#endregion + //#region Joh - selection range provider export class SelectionRangeKind { diff --git a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts index 4c20602bd21..752cba581d5 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts @@ -8,6 +8,8 @@ import { MainThreadWindowShape, ExtHostWindowShape, ExtHostContext, MainContext, import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { Event } from 'vs/base/common/event'; +import { UriComponents, URI } from 'vs/base/common/uri'; +import { ICommandService } from 'vs/platform/commands/common/commands'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -17,7 +19,8 @@ export class MainThreadWindow implements MainThreadWindowShape { constructor( extHostContext: IExtHostContext, - @IWindowService private readonly windowService: IWindowService + @IWindowService private readonly windowService: IWindowService, + @ICommandService private readonly commandService: ICommandService, ) { this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow); @@ -25,11 +28,17 @@ export class MainThreadWindow implements MainThreadWindowShape { (this.proxy.$onDidChangeWindowFocus, this.proxy, this.disposables); } + dispose(): void { + this.disposables = dispose(this.disposables); + } + $getWindowVisibility(): Promise { return this.windowService.isFocused(); } - dispose(): void { - this.disposables = dispose(this.disposables); + $openUri(uri: UriComponents): Promise { + // todo@joh turn this around and let the command work with + // the proper implementation + return this.commandService.executeCommand('_workbench.open', URI.revive(uri)); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index efaabac633b..6cb7c089487 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -497,6 +497,9 @@ export function createApiFactory( createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); }, + open: proposedApiFunction(extension, (uri: URI) => { + return extHostWindow.openUri(uri); + }) }; // namespace: workspace diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 9a26e5fa431..04635c90791 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -621,6 +621,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; + $openUri(uri: UriComponents): Promise; } // -- extension host diff --git a/src/vs/workbench/api/node/extHostWindow.ts b/src/vs/workbench/api/node/extHostWindow.ts index 7eafd3bd06b..e995982451a 100644 --- a/src/vs/workbench/api/node/extHostWindow.ts +++ b/src/vs/workbench/api/node/extHostWindow.ts @@ -6,6 +6,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext } from './extHost.protocol'; import { WindowState } from 'vscode'; +import { URI } from 'vs/base/common/uri'; export class ExtHostWindow implements ExtHostWindowShape { @@ -34,4 +35,8 @@ export class ExtHostWindow implements ExtHostWindowShape { this._state = { ...this._state, focused }; this._onDidChangeWindowState.fire(this._state); } + + openUri(uri: URI): Promise { + return this._proxy.$openUri(uri); + } } From f8719df33fb9dba4404e62313137a937e3dab739 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 18 Jan 2019 20:36:42 +0100 Subject: [PATCH 0404/1066] list: relocate widget --- src/vs/base/browser/ui/list/list.css | 29 ++++++- src/vs/base/browser/ui/list/listWidget.ts | 2 +- src/vs/base/browser/ui/tree/abstractTree.ts | 92 ++++++++++++++++++--- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 2c5841a67c0..05423c95419 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -71,8 +71,6 @@ .monaco-list-type-filter { position: absolute; - /* right: 2px; - top: 1px; */ border-radius: 2px; border-width: 1px; border-style: solid; @@ -83,8 +81,33 @@ text-align: right; box-sizing: border-box; animation: monaco-list-type-filter-fade-in 150ms linear 0s; + cursor: all-scroll; font-size: 13px; line-height: 18px; height: 20px; - z-index: 100; + z-index: 1; +} + +.monaco-list-type-filter.animate { + transition: top 0.2s, left 0.2s; +} + +.monaco-list-type-filter.ne { + top: 4px; + right: 4px; +} + +.monaco-list-type-filter.nw { + top: 4px; + left: 4px; +} + +.monaco-list-type-filter.se { + bottom: 4px; + right: 4px; +} + +.monaco-list-type-filter.sw { + bottom: 4px; + left: 4px; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 279555123f6..df91bef1758 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -734,7 +734,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listMatchesShadow) { - content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 2px ${styles.listMatchesShadow}; }`); + content.push(`.monaco-list-type-filter { box-shadow: 1px 1px 1px ${styles.listMatchesShadow}; }`); } const newStyles = content.join('\n'); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 545cd8d1f0d..5466d0ebd0f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, timeout, getDomNodePagePosition } from 'vs/base/browser/dom'; +import { append, $, toggleClass, timeout, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -331,7 +331,9 @@ class TypeFilterController implements IDisposable { private _onDidChangePattern = new Emitter(); readonly onDidChangePattern = this._onDidChangePattern.event; + private positionClassName = 'ne'; private domNode: HTMLElement; + private _pattern = ''; private disposables: IDisposable[] = []; @@ -341,7 +343,9 @@ class TypeFilterController implements IDisposable { private filter: TypeFilter, keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider ) { - this.domNode = $('.monaco-list-type-filter'); + this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); + this.domNode.draggable = true; + domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onInput = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) @@ -369,25 +373,91 @@ class TypeFilterController implements IDisposable { const container = this.view.getHTMLElement(); if (pattern && !this.domNode.parentElement) { - const { top, left } = getDomNodePagePosition(container); - - this.domNode.style.top = `${top - 22}px`; - this.domNode.style.left = `${left + 2}px`; - this.domNode.style.maxWidth = `${container.clientWidth}px`; - document.body.append(this.domNode); - + container.append(this.domNode); } else if (!pattern && this.domNode.parentElement) { this.domNode.remove(); } + this.domNode.textContent = pattern.length > 16 + ? '…' + pattern.substr(pattern.length - 16) + : pattern; + this._pattern = pattern; - this.domNode.textContent = pattern; this.filter.pattern = pattern; this.tree.refilter(); this._onDidChangePattern.fire(pattern); } + private onDragStart(event: DragEvent): void { + const container = this.view.getHTMLElement(); + const { top, left } = getDomNodePagePosition(container); + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; + const midContainerWidth = containerWidth / 2; + const midContainerHeight = containerHeight / 2; + const width = this.domNode.clientWidth; + const height = this.domNode.clientHeight; + const disposables: IDisposable[] = []; + let positionClassName = this.positionClassName; + + const updatePosition = () => { + switch (positionClassName) { + case 'nw': + this.domNode.style.top = `4px`; + this.domNode.style.left = `4px`; + break; + case 'ne': + this.domNode.style.top = `4px`; + this.domNode.style.left = `${containerWidth - width - 6}px`; + break; + case 'sw': + this.domNode.style.top = `${containerHeight - height - 6}px`; + this.domNode.style.left = `4px`; + break; + case 'se': + this.domNode.style.top = `${containerHeight - height - 6}px`; + this.domNode.style.left = `${containerWidth - width - 6}px`; + break; + } + }; + + const onDragOver = (event: DragEvent) => { + const x = event.screenX - left; + const y = event.screenY - top; + + if (x < midContainerWidth && y < midContainerHeight) { + positionClassName = 'nw'; + } else if (x >= midContainerWidth && y < midContainerHeight) { + positionClassName = 'ne'; + } else if (x < midContainerWidth && y >= midContainerHeight) { + positionClassName = 'sw'; + } else { + positionClassName = 'se'; + } + + updatePosition(); + }; + + const onDragEnd = () => { + this.positionClassName = positionClassName; + this.domNode.className = `monaco-list-type-filter ${this.positionClassName}`; + this.domNode.style.top = null; + this.domNode.style.left = null; + + dispose(disposables); + }; + + updatePosition(); + removeClass(this.domNode, positionClassName); + + addClass(this.domNode, 'animate'); + disposables.push(toDisposable(() => removeClass(this.domNode, 'animate'))); + + domEvent(document, 'dragover')(onDragOver, null, disposables); + domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); + } + dispose() { this.disposables = dispose(this.disposables); } From 442cf886ac4c69f3c4caee61d5f7fecd1968cf01 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 18 Jan 2019 23:54:59 +0100 Subject: [PATCH 0405/1066] fix strict null check --- src/vs/workbench/services/label/common/labelService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index dfab0c043e7..c4cb59dede6 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -87,7 +87,7 @@ class ResourceLabelFormattersHandler implements IWorkbenchContribution { this.formattersDisposables.set(formatter, labelService.registerFormatter(formatter)); })); delta.removed.forEach(removed => removed.value.forEach(formatter => { - this.formattersDisposables.get(formatter).dispose(); + this.formattersDisposables.get(formatter)!.dispose(); })); }); } From dd11771be8b29f403f584cb90df597b91c7aa775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Pirac=C3=A9s=20Alastuey?= Date: Sat, 19 Jan 2019 00:20:03 +0100 Subject: [PATCH 0406/1066] Add strict null checks to suggestMemory.test.ts --- src/tsconfig.strictNullChecks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 08bfbe946ed..f801b9fe24d 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -249,6 +249,7 @@ "./vs/editor/contrib/referenceSearch/test/referencesModel.test.ts", "./vs/editor/contrib/rename/rename.ts", "./vs/editor/contrib/rename/renameInputField.ts", + "./vs/editor/contrib/suggest/test/suggestMemory.test.ts", "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", "./vs/editor/contrib/wordOperations/test/wordOperations.test.ts", From 84162b715b7f0f3739978c020760b4597df60a35 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Jan 2019 16:52:06 -0800 Subject: [PATCH 0407/1066] add command to list of disallowed schemes --- src/vs/base/common/network.ts | 2 ++ src/vs/workbench/api/node/extHostFileSystem.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 1915b8fb6f3..122a715c32d 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -42,4 +42,6 @@ export namespace Schemas { export const untitled: string = 'untitled'; export const data: string = 'data'; + + export const command: string = 'command'; } diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 2fd8bd78205..9753a6ed749 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -127,6 +127,7 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { this._usedSchemes.add(Schemas.https); this._usedSchemes.add(Schemas.mailto); this._usedSchemes.add(Schemas.data); + this._usedSchemes.add(Schemas.command); } dispose(): void { From f9d0c4b2cb92330e545b19760125c094ef5690eb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Jan 2019 18:51:39 -0800 Subject: [PATCH 0408/1066] wire up read/write correctly, #66759 --- .../api/electron-browser/mainThreadFileSystem.ts | 7 +++++-- src/vs/workbench/api/node/extHost.protocol.ts | 4 ++-- src/vs/workbench/api/node/extHostFileSystem.ts | 11 +++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index 3ad9bff063c..0a948e1cfbb 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -139,10 +139,13 @@ class RemoteFileSystemProvider implements IFileSystemProvider { } read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { - return this._proxy.$read(this._handle, fd, pos, RemoteFileSystemProvider._asBuffer(data), offset, length); + return this._proxy.$read(this._handle, fd, pos, length).then(readData => { + data.set(readData, offset); + return readData.byteLength; + }); } write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise { - return this._proxy.$write(this._handle, fd, pos, RemoteFileSystemProvider._asBuffer(data), offset, length); + return this._proxy.$write(this._handle, fd, pos, Buffer.from(data, offset, length)); } } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 04635c90791..d8e363a64a2 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -726,8 +726,8 @@ export interface ExtHostFileSystemShape { $unwatch(handle: number, session: number): void; $open(handle: number, resource: UriComponents): Promise; $close(handle: number, fd: number): Promise; - $read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise; - $write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise; + $read(handle: number, fd: number, pos: number, length: number): Promise; + $write(handle: number, fd: number, pos: number, data: Buffer): Promise; } export interface ExtHostSearchShape { diff --git a/src/vs/workbench/api/node/extHostFileSystem.ts b/src/vs/workbench/api/node/extHostFileSystem.ts index 9753a6ed749..f1fe101d1c1 100644 --- a/src/vs/workbench/api/node/extHostFileSystem.ts +++ b/src/vs/workbench/api/node/extHostFileSystem.ts @@ -290,14 +290,17 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape { return Promise.resolve(this._fsProvider.get(handle).close(fd)); } - $read(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise { + $read(handle: number, fd: number, pos: number, length: number): Promise { this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, offset, length)); + const data = Buffer.allocUnsafe(length); + return Promise.resolve(this._fsProvider.get(handle).read(fd, pos, data, 0, length)).then(read => { + return data.slice(0, read); // don't send zeros + }); } - $write(handle: number, fd: number, pos: number, data: Buffer, offset: number, length: number): Promise { + $write(handle: number, fd: number, pos: number, data: Buffer): Promise { this._checkProviderExists(handle); - return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, offset, length)); + return Promise.resolve(this._fsProvider.get(handle).write(fd, pos, data, 0, data.length)); } } From bb6f5387c354165abc35695e58373cfb1ec38099 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 18 Jan 2019 19:17:38 -0800 Subject: [PATCH 0409/1066] make sure to start writing at 0 #66759 --- src/vs/workbench/services/files/electron-browser/streams.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index eabf557773a..9b44d0c78a4 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -43,7 +43,7 @@ function createSimpleWritable(provider: IFileSystemProvider, resource: URI, opts function createWritable(provider: IFileSystemProvider, resource: URI, opts: FileWriteOptions): Writable { return new class extends Writable { _fd: number; - _pos: number; + _pos: number = 0; constructor(opts?) { super(opts); } From 2934fcecee1d595bc5ee79aead9b19a41502d5e8 Mon Sep 17 00:00:00 2001 From: Asaf Cohen Date: Sat, 19 Jan 2019 14:47:03 +0200 Subject: [PATCH 0410/1066] 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 c70c49c222faf0e98d53b22b14c7d4dcb7d48cca Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Sat, 19 Jan 2019 10:02:48 -0800 Subject: [PATCH 0411/1066] don't reuse buffer, #66759 --- src/vs/workbench/services/files/electron-browser/streams.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index 9b44d0c78a4..9892b64364b 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -97,8 +97,8 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: if (typeof this._fd !== 'number') { this._fd = await provider.open(resource); } - let buffer = Buffer.allocUnsafe(64 * 1024); while (this._reading) { + let buffer = Buffer.allocUnsafe(64 * 1024); let bytesRead = await provider.read(this._fd, this._pos, buffer, 0, buffer.length); if (bytesRead === 0) { this._reading = false; From 1934d4b269cc5bb3dfaf72e2c00118a3567e9030 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Sat, 19 Jan 2019 10:31:56 -0800 Subject: [PATCH 0412/1066] make sure to close files after read, adopt newer streams api, #66759 --- src/vs/base/node/encoding.ts | 14 +++------- .../files/electron-browser/streams.ts | 26 +++++++------------ 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/src/vs/base/node/encoding.ts b/src/vs/base/node/encoding.ts index 069997e72b7..8071f13ecd1 100644 --- a/src/vs/base/node/encoding.ts +++ b/src/vs/base/node/encoding.ts @@ -7,7 +7,7 @@ import * as stream from 'vs/base/node/stream'; import * as iconv from 'iconv-lite'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { exec } from 'child_process'; -import { Readable, Writable, WritableOptions } from 'stream'; +import { Readable, Writable } from 'stream'; export const UTF8 = 'utf8'; export const UTF8_with_bom = 'utf8bom'; @@ -40,11 +40,6 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions private _buffer: Buffer[] = []; private _bytesBuffered = 0; - constructor(opts?: WritableOptions) { - super(opts); - this.once('finish', () => this._finish()); - } - _write(chunk: any, encoding: string, callback: Function): void { if (!Buffer.isBuffer(chunk)) { callback(new Error('data must be a buffer')); @@ -93,14 +88,13 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions callback(err); }); } - - _finish(): void { + _final(callback: (err?: any) => any) { if (this._decodeStream) { // normal finish - this._decodeStream.end(); + this._decodeStream.end(callback); } else { // we were still waiting for data... - this._startDecodeStream(() => this._decodeStream.end()); + this._startDecodeStream(() => this._decodeStream.end(callback)); } } }); diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index 9892b64364b..60117fe986d 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -59,12 +59,8 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File callback(err); } } - end() { - provider.close(this._fd).then(_ => { - super.end(); - }, err => { - this.emit('error', err); - }); + _final(callback: (err?: any) => any) { + provider.close(this._fd).then(callback, callback); } }; } @@ -84,10 +80,7 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: _fd: number; _pos: number = position; _reading: boolean = false; - constructor(opts?) { - super(opts); - this.once('close', _ => this._final()); - } + async _read(size?: number) { if (this._reading) { return; @@ -98,26 +91,25 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: this._fd = await provider.open(resource); } while (this._reading) { - let buffer = Buffer.allocUnsafe(64 * 1024); + let buffer = Buffer.allocUnsafe(size); let bytesRead = await provider.read(this._fd, this._pos, buffer, 0, buffer.length); if (bytesRead === 0) { + await provider.close(this._fd); this._reading = false; this.push(null); - } - else { + } else { this._reading = this.push(buffer.slice(0, bytesRead)); this._pos += bytesRead; } } - } - catch (err) { + } catch (err) { // this.emit('error', err); } } - async _final() { + _destroy(_err: any, callback: (err?: any) => any) { if (typeof this._fd === 'number') { - await provider.close(this._fd); + provider.close(this._fd).then(callback, callback); } } }; From 9969ff283f48c2e2d7d08fb93b09fa6e1bf2b664 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Sat, 19 Jan 2019 10:46:09 -0800 Subject: [PATCH 0413/1066] filter - make dollar sign a strong character --- src/vs/base/common/filters.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 3b128cddf7b..29c047955f9 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -462,6 +462,7 @@ function isSeparatorAtPos(value: string, index: number): boolean { case CharCode.SingleQuote: case CharCode.DoubleQuote: case CharCode.Colon: + case CharCode.DollarSign: return true; default: return false; From f39fdf8c5a733212f0c4c39ef1b13d0369e23efc Mon Sep 17 00:00:00 2001 From: g1tman <32861951+g1tman@users.noreply.github.com> Date: Sun, 20 Jan 2019 00:17:51 +0530 Subject: [PATCH 0414/1066] Update terminalInstance.ts --- .../parts/terminal/electron-browser/terminalInstance.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 3560ab6ebdd..3b51fc9f5d8 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -1181,16 +1181,15 @@ export class TerminalInstance implements ITerminalInstance { let cols = this._cols; let rows = this._rows; if (this._dimensionsOverride && this._dimensionsOverride.cols && this._dimensionsOverride.rows) { - cols = Math.min(Math.max(this._dimensionsOverride.cols, 2), this._cols); - rows = Math.min(Math.max(this._dimensionsOverride.rows, 2), this._rows); + cols = Math.min(Math.max(this._dimensionsOverride.cols, 2), cols); + rows = Math.min(Math.max(this._dimensionsOverride.rows, 2), rows); } if (this._xterm) { - const font = this._configHelper.getFont(this._xterm); - // Only apply these settings when the terminal is visible so that // the characters are measured correctly. if (this._isVisible) { + const font = this._configHelper.getFont(this._xterm); const config = this._configHelper.config; this._safeSetOption('letterSpacing', font.letterSpacing); this._safeSetOption('lineHeight', font.lineHeight); From 4e977c5738a8e4047d4ca19d097964f5db5d3550 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sun, 20 Jan 2019 11:36:51 -0800 Subject: [PATCH 0415/1066] Update node-pty, enable conpty for Windows 18309+ Fixes #66158 --- package.json | 2 +- .../electron-browser/terminal.contribution.ts | 4 ++-- .../parts/terminal/node/terminalProcess.ts | 12 +++++++++- yarn.lock | 23 +++++++++++-------- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index ca7ec411751..58456599d0d 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "native-is-elevated": "^0.2.1", "native-keymap": "1.2.5", "native-watchdog": "1.0.0", - "node-pty": "0.8.0", + "node-pty": "0.8.1", "semver": "^5.5.0", "spdlog": "0.7.2", "sudo-prompt": "8.2.0", 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 5e1fb9750ee..e016e1e90f5 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminal.contribution.ts @@ -283,9 +283,9 @@ configurationRegistry.registerConfiguration({ default: 'inherited' }, 'terminal.integrated.windowsEnableConpty': { - description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 17692+). Winpty will be used if this is false."), + description: nls.localize('terminal.integrated.windowsEnableConpty', "Whether to use ConPTY for Windows terminal process communication (requires Windows 10 build number 18309+). Winpty will be used if this is false."), type: 'boolean', - default: false + default: true } } }); diff --git a/src/vs/workbench/parts/terminal/node/terminalProcess.ts b/src/vs/workbench/parts/terminal/node/terminalProcess.ts index 53c92cc4bfa..ac913edc443 100644 --- a/src/vs/workbench/parts/terminal/node/terminalProcess.ts +++ b/src/vs/workbench/parts/terminal/node/terminalProcess.ts @@ -47,13 +47,14 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { shellName = 'xterm-256color'; } + const useConpty = windowsEnableConpty && process.platform === 'win32' && this._getWindowsBuildNumber() >= 18309; const options: pty.IPtyForkOptions = { name: shellName, cwd, env, cols, rows, - experimentalUseConpty: windowsEnableConpty + experimentalUseConpty: useConpty }; try { @@ -101,6 +102,15 @@ export class TerminalProcess implements ITerminalChildProcess, IDisposable { this._onProcessTitleChanged.dispose(); } + private _getWindowsBuildNumber(): number { + const osVersion = (/(\d+)\.(\d+)\.(\d+)/g).exec(os.release()); + let buildNumber: number = 0; + if (osVersion && osVersion.length === 4) { + buildNumber = parseInt(osVersion[3]); + } + return buildNumber; + } + private _setupTitlePolling() { // Send initial timeout async to give event listeners a chance to init setTimeout(() => { diff --git a/yarn.lock b/yarn.lock index a02896fb7f3..9dbb3c2151d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5855,10 +5855,10 @@ mute-stream@0.0.7, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= -nan@2.10.0, nan@^2.9.2, nan@~2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== +nan@2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" + integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== nan@2.8.0, nan@^2.8.0: version "2.8.0" @@ -5870,6 +5870,11 @@ nan@^2.10.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.11.0.tgz#574e360e4d954ab16966ec102c0c049fd961a099" integrity sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw== +nan@^2.9.2, nan@~2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -5988,12 +5993,12 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-pty@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.0.tgz#08bccb633f49e2e3f7245eb56ea6b40f37ccd64f" - integrity sha512-g5ggk3gN4gLrDmAllee5ScFyX3YzpOC/U8VJafha4pE7do0TIE1voiIxEbHSRUOPD1xYqmY+uHhOKAd3avbxGQ== +node-pty@0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f" + integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw== dependencies: - nan "2.10.0" + nan "2.12.1" node-uuid@~1.4.0, node-uuid@~1.4.7: version "1.4.8" From 444b8f0fcf28eb808e75dbc2e529a1481e77a22c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 08:54:09 +0100 Subject: [PATCH 0416/1066] cleanup imports --- src/vs/platform/theme/common/styler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 533a0e8a32a..3b8f646d040 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, editorFindMatchHighlightBorder, darken, editorWidgetBackground, blend2 } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, darken, editorWidgetBackground, blend2 } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; From 3d9f9c0d03a38c840795bdc146b6a722a279ef90 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 09:08:15 +0100 Subject: [PATCH 0417/1066] prevent typelabel from scrolling list --- src/vs/base/browser/dnd.ts | 13 +++++++++++++ src/vs/base/browser/ui/list/listView.ts | 5 +++++ src/vs/base/browser/ui/tree/abstractTree.ts | 5 ++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index e7703182722..3aca5c59adf 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -90,6 +90,19 @@ export interface IDragAndDropData { getData(): any; } +export class DragAndDropData implements IDragAndDropData { + + constructor(private data: T) { } + + update(): void { + // noop + } + + getData(): T { + return this.data; + } +} + export interface IStaticDND { CurrentDragAndDropData: IDragAndDropData | undefined; } diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 5623ac4c122..844e080672f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -636,6 +636,11 @@ export class ListView implements ISpliceable, IDisposable { private onDragOver(event: IListDragEvent): boolean { this.onDragLeaveTimeout.dispose(); + + if (StaticDND.CurrentDragAndDropData && StaticDND.CurrentDragAndDropData.getData() === 'vscode-ui') { + return false; + } + this.setupDragAndDropScrollTopAnimation(event.browserEvent); if (!event.browserEvent.dataTransfer) { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5466d0ebd0f..e21f3373dc5 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -13,7 +13,7 @@ import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardE import { KeyCode } from 'vs/base/common/keyCodes'; import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; import { range } from 'vs/base/common/arrays'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { domEvent } from 'vs/base/browser/event'; @@ -456,6 +456,9 @@ class TypeFilterController implements IDisposable { domEvent(document, 'dragover')(onDragOver, null, disposables); domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); + + StaticDND.CurrentDragAndDropData = new DragAndDropData('vscode-ui'); + disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); } dispose() { From e733f8a6f871be881c97fd2d54492205d9fc783a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 09:32:01 +0100 Subject: [PATCH 0418/1066] filter on type checkbox --- src/vs/base/browser/ui/list/list.css | 1 + src/vs/base/browser/ui/tree/abstractTree.ts | 52 ++++++++++++++++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 05423c95419..344669fc1c4 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -70,6 +70,7 @@ } .monaco-list-type-filter { + display: flex; position: absolute; border-radius: 2px; border-width: 1px; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e21f3373dc5..69b97c74262 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -278,6 +278,7 @@ class TypeFilter implements ITreeFilter { } constructor( + private tree: AbstractTree, private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider, private _filter?: ITreeFilter ) { } @@ -308,14 +309,14 @@ class TypeFilter implements ITreeFilter { const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, label, label.toLowerCase(), 0, true); if (!score) { - // DEMO: just highlights - return { data: FuzzyScore.Default, visibility: true }; + if (this.tree.configuration.filterOnType) { + return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + } else { + return { data: FuzzyScore.Default, visibility: true }; + } // DEMO: filter // return TreeVisibility.Recurse; - - // DEMO: smarter filter - // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } return { data: score, visibility: true }; @@ -333,6 +334,7 @@ class TypeFilterController implements IDisposable { private positionClassName = 'ne'; private domNode: HTMLElement; + private label: HTMLElement; private _pattern = ''; private disposables: IDisposable[] = []; @@ -347,6 +349,14 @@ class TypeFilterController implements IDisposable { this.domNode.draggable = true; domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); + this.label = append(this.domNode, $('span.label')); + + const controls = append(this.domNode, $('.controls')); + const filterOnType = append(controls, $('input')); + filterOnType.type = 'checkbox'; + filterOnType.checked = !!tree.configuration.filterOnType; + Event.map(domEvent(filterOnType, 'input'), _ => filterOnType.checked)(this.onDidChangeFilterOnType, this, this.disposables); + const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onInput = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) @@ -378,7 +388,7 @@ class TypeFilterController implements IDisposable { this.domNode.remove(); } - this.domNode.textContent = pattern.length > 16 + this.label.textContent = pattern.length > 16 ? '…' + pattern.substr(pattern.length - 16) : pattern; @@ -389,7 +399,7 @@ class TypeFilterController implements IDisposable { this._onDidChangePattern.fire(pattern); } - private onDragStart(event: DragEvent): void { + private onDragStart(): void { const container = this.view.getHTMLElement(); const { top, left } = getDomNodePagePosition(container); const containerWidth = container.clientWidth; @@ -461,6 +471,10 @@ class TypeFilterController implements IDisposable { disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); } + onDidChangeFilterOnType(filterOnType: boolean): void { + this.tree.updateConfiguration({ filterOnType }); + } + dispose() { this.disposables = dispose(this.disposables); } @@ -492,7 +506,9 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IAbstractTreeOptionsUpdate, IListOptions { readonly collapseByDefault?: boolean; // defaults to false @@ -501,8 +517,8 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly autoExpandSingleChildren?: boolean; } -export interface IAbstractTreeWithTypeFilterOptions extends IAbstractTreeOptions { - readonly keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider; +export interface IAbstractTreeConfiguration { + readonly filterOnType: boolean; } export abstract class AbstractTree implements IDisposable { @@ -510,6 +526,7 @@ export abstract class AbstractTree implements IDisposable private view: List>; private renderers: TreeRenderer[]; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; + private _configuration: IAbstractTreeConfiguration; protected model: ITreeModel; protected disposables: IDisposable[] = []; @@ -545,10 +562,14 @@ export abstract class AbstractTree implements IDisposable this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, options)); this.disposables.push(...this.renderers); + this._configuration = { + filterOnType: typeof options.filterOnType === 'boolean' ? options.filterOnType : false + }; + let filter: ITreeFilter | undefined; if (options.keyboardNavigationLabelProvider) { - filter = new TypeFilter(options.keyboardNavigationLabelProvider, options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here + filter = new TypeFilter(this, options.keyboardNavigationLabelProvider, options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here options = { ...options, filter }; } @@ -584,6 +605,15 @@ export abstract class AbstractTree implements IDisposable } } + updateConfiguration(update: Partial): void { + this._configuration = { ...this._configuration, ...update }; + this.refilter(); + } + + get configuration(): IAbstractTreeConfiguration { + return this._configuration; + } + // Widget getHTMLElement(): HTMLElement { From 042bda1dd6a086d1dfd0e47dec5c46109e4f71ff Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 09:33:18 +0100 Subject: [PATCH 0419/1066] fix keydown on filter on type --- 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 69b97c74262..cfe6195c348 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -359,7 +359,7 @@ class TypeFilterController implements IDisposable { const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onInput = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) - .filter(e => !isInputElement(e.target as HTMLElement)) + .filter(e => !isInputElement(e.target as HTMLElement) || e.target === filterOnType) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e)) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) From f21a18f9f9a56fe61a555f8325fda1406029934b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 10:42:35 +0100 Subject: [PATCH 0420/1066] filter on type: enable, disable, animation --- src/vs/base/browser/ui/list/list.css | 66 ++++++++++++++++++- .../base/browser/ui/list/media/close-dark.svg | 1 + .../base/browser/ui/list/media/close-hc.svg | 33 ++++++++++ src/vs/base/browser/ui/list/media/close.svg | 1 + .../browser/ui/list/media/filter-dark.svg | 51 ++++++++++++++ .../base/browser/ui/list/media/filter-hc.svg | 51 ++++++++++++++ src/vs/base/browser/ui/list/media/filter.svg | 51 ++++++++++++++ .../browser/ui/list/media/no-filter-dark.svg | 51 ++++++++++++++ .../browser/ui/list/media/no-filter-hc.svg | 51 ++++++++++++++ .../base/browser/ui/list/media/no-filter.svg | 51 ++++++++++++++ src/vs/base/browser/ui/tree/abstractTree.ts | 46 +++++++++---- 11 files changed, 440 insertions(+), 13 deletions(-) create mode 100644 src/vs/base/browser/ui/list/media/close-dark.svg create mode 100644 src/vs/base/browser/ui/list/media/close-hc.svg create mode 100644 src/vs/base/browser/ui/list/media/close.svg create mode 100644 src/vs/base/browser/ui/list/media/filter-dark.svg create mode 100644 src/vs/base/browser/ui/list/media/filter-hc.svg create mode 100644 src/vs/base/browser/ui/list/media/filter.svg create mode 100644 src/vs/base/browser/ui/list/media/no-filter-dark.svg create mode 100644 src/vs/base/browser/ui/list/media/no-filter-hc.svg create mode 100644 src/vs/base/browser/ui/list/media/no-filter.svg diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 344669fc1c4..4037fa1e5ff 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -89,7 +89,7 @@ z-index: 1; } -.monaco-list-type-filter.animate { +.monaco-list-type-filter.dragging { transition: top 0.2s, left 0.2s; } @@ -111,4 +111,68 @@ .monaco-list-type-filter.sw { bottom: 4px; left: 4px; +} + +.monaco-list-type-filter > .controls { + display: flex; + align-items: center; + box-sizing: border-box; + transition: width 0.2s; + width: 0; +} + +.monaco-list-type-filter.dragging > .controls, +.monaco-list-type-filter:hover > .controls { + width: 36px; +} + +.monaco-list-type-filter > .controls > * { + box-sizing: border-box; + width: 16px; + height: 16px; + margin: 0 0 0 2px; + flex-shrink: 0; +} + +.monaco-list-type-filter > .controls > .filter { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: url("media/no-filter.svg"); + background-position: 50% 50%; + cursor: pointer; +} + +.monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-dark.svg"); +} + +.vs-dark .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter { + background-image: url("media/no-filter-hc.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .filter:checked { + background-image: url("media/filter-hc.svg"); +} + +.monaco-list-type-filter > .controls > .clear { + border: none; + background: url("media/close.svg"); + cursor: pointer; +} + +.vs-dark .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-dark.svg"); +} + +.hc-black .monaco-list-type-filter > .controls > .clear { + background-image: url("media/close-hc.svg"); } \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/media/close-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg new file mode 100644 index 00000000000..751e89b3b02 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/close-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg new file mode 100644 index 00000000000..c20895c60aa --- /dev/null +++ b/src/vs/base/browser/ui/list/media/close-hc.svg @@ -0,0 +1,33 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/close.svg b/src/vs/base/browser/ui/list/media/close.svg new file mode 100644 index 00000000000..fde34404d4e --- /dev/null +++ b/src/vs/base/browser/ui/list/media/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg new file mode 100644 index 00000000000..67a29ec0eeb --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-dark.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg new file mode 100644 index 00000000000..b81a030f51b --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter-hc.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/filter.svg b/src/vs/base/browser/ui/list/media/filter.svg new file mode 100644 index 00000000000..7d7d5f4fd35 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/filter.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg new file mode 100644 index 00000000000..2189e00d559 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-dark.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg new file mode 100644 index 00000000000..173adcfa511 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter-hc.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter.svg b/src/vs/base/browser/ui/list/media/no-filter.svg new file mode 100644 index 00000000000..55141722d66 --- /dev/null +++ b/src/vs/base/browser/ui/list/media/no-filter.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index cfe6195c348..c0ada858c5b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -19,6 +19,7 @@ import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { domEvent } from 'vs/base/browser/event'; import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters'; import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTreeModel'; +import { localize } from 'vs/nls'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -335,6 +336,7 @@ class TypeFilterController implements IDisposable { private positionClassName = 'ne'; private domNode: HTMLElement; private label: HTMLElement; + private filterOnType: HTMLInputElement; private _pattern = ''; private disposables: IDisposable[] = []; @@ -350,21 +352,31 @@ class TypeFilterController implements IDisposable { domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); this.label = append(this.domNode, $('span.label')); - const controls = append(this.domNode, $('.controls')); - const filterOnType = append(controls, $('input')); - filterOnType.type = 'checkbox'; - filterOnType.checked = !!tree.configuration.filterOnType; - Event.map(domEvent(filterOnType, 'input'), _ => filterOnType.checked)(this.onDidChangeFilterOnType, this, this.disposables); + + this.filterOnType = append(controls, $('input.filter')); + this.filterOnType.type = 'checkbox'; + this.filterOnType.checked = !!tree.configuration.filterOnType; + this.filterOnType.tabIndex = -1; + this.updateFilterOnTypeTitle(); + domEvent(this.filterOnType, 'input')(this.onDidChangeFilterOnType, this, this.disposables); + + const clear = append(controls, $('button.clear')); + clear.tabIndex = -1; + clear.title = localize('clear', "Clear"); + const onClear = domEvent(clear, 'click'); const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); - const onInput = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) - .filter(e => !isInputElement(e.target as HTMLElement) || e.target === filterOnType) + const onKeyDown = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) + .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnType) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.Backspace || e.keyCode === KeyCode.Escape || isPrintableCharEvent(e)) .forEach(e => { e.stopPropagation(); e.preventDefault(); }) + .event; + + const onInput = Event.chain(Event.any(onKeyDown, onClear)) .reduce((previous: string, e) => { - if (e.keyCode === KeyCode.Escape) { + if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape) { return ''; } @@ -386,6 +398,7 @@ class TypeFilterController implements IDisposable { container.append(this.domNode); } else if (!pattern && this.domNode.parentElement) { this.domNode.remove(); + this.tree.domFocus(); } this.label.textContent = pattern.length > 16 @@ -461,8 +474,8 @@ class TypeFilterController implements IDisposable { updatePosition(); removeClass(this.domNode, positionClassName); - addClass(this.domNode, 'animate'); - disposables.push(toDisposable(() => removeClass(this.domNode, 'animate'))); + addClass(this.domNode, 'dragging'); + disposables.push(toDisposable(() => removeClass(this.domNode, 'dragging'))); domEvent(document, 'dragover')(onDragOver, null, disposables); domEvent(this.domNode, 'dragend')(onDragEnd, null, disposables); @@ -471,8 +484,17 @@ class TypeFilterController implements IDisposable { disposables.push(toDisposable(() => StaticDND.CurrentDragAndDropData = undefined)); } - onDidChangeFilterOnType(filterOnType: boolean): void { - this.tree.updateConfiguration({ filterOnType }); + private onDidChangeFilterOnType(): void { + this.tree.updateConfiguration({ filterOnType: this.filterOnType.checked }); + this.updateFilterOnTypeTitle(); + } + + private updateFilterOnTypeTitle(): void { + if (this.filterOnType.checked) { + this.filterOnType.title = localize('disable filter on type', "Disable Filter on Type"); + } else { + this.filterOnType.title = localize('enable filter on type', "Enable Filter on Type"); + } } dispose() { From a42afaf801224997333d89c7be7910b93c20ca6e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 10:46:36 +0100 Subject: [PATCH 0421/1066] remove fade animation --- src/vs/base/browser/ui/list/list.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 4037fa1e5ff..171d2ff73a3 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -64,10 +64,6 @@ } /* Type filter */ -@keyframes monaco-list-type-filter-fade-in { - 0% { opacity: 0; } - 100% { opacity: 1; } -} .monaco-list-type-filter { display: flex; @@ -81,7 +77,6 @@ overflow: hidden; text-align: right; box-sizing: border-box; - animation: monaco-list-type-filter-fade-in 150ms linear 0s; cursor: all-scroll; font-size: 13px; line-height: 18px; From e75e51695c310b7cef7a1418aff81fa12eb5117b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 11:18:18 +0100 Subject: [PATCH 0422/1066] focus tree on filter switch --- src/vs/base/browser/ui/tree/abstractTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index c0ada858c5b..6d9db831981 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -486,6 +486,7 @@ class TypeFilterController implements IDisposable { private onDidChangeFilterOnType(): void { this.tree.updateConfiguration({ filterOnType: this.filterOnType.checked }); + this.tree.domFocus(); this.updateFilterOnTypeTitle(); } From 14b71413b186c91222cd535f1c8ac82378d094bc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Jan 2019 11:31:44 +0100 Subject: [PATCH 0423/1066] storage - improve perf by chunking INSERTS/DELETES --- src/vs/base/common/arrays.ts | 4 +- src/vs/base/node/storage.ts | 84 ++++++++++++++----- src/vs/base/test/common/arrays.test.ts | 10 ++- src/vs/base/test/node/storage/storage.test.ts | 64 ++++++++++++++ 4 files changed, 136 insertions(+), 26 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 07c888aa56d..ab01e07bded 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -439,9 +439,9 @@ export function range(arg: number, to?: number): number[] { return result; } -export function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { +export function fill(num: number, value: T, arr: T[] = []): T[] { for (let i = 0; i < num; i++) { - arr[i] = valueFn(); + arr[i] = value; } return arr; diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 316b130217c..5cb7369ef99 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -12,6 +12,7 @@ import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; import { mark } from 'vs/base/common/performance'; import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; +import { fill } from 'vs/base/common/arrays'; export enum StorageHint { @@ -328,6 +329,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY + private static MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement private path: string; private name: string; @@ -383,35 +385,71 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } return this.transaction(connection, () => { - if (request.insert && request.insert.size > 0) { - this.prepare(connection, 'INSERT INTO ItemTable VALUES (?,?)', stmt => { - request.insert!.forEach((value, key) => { - stmt.run([key, value]); - }); - }, () => { - const keys: string[] = []; - let length = 0; - request.insert!.forEach((value, key) => { - keys.push(key); - length += value.length; - }); - return `Keys: ${keys.join(', ')} Length: ${length}`; + // INSERT + if (request.insert && request.insert.size > 0) { + const keysValuesChunks: (string[])[] = []; + keysValuesChunks.push([]); // seed with initial empty chunk + + // Split key/values into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the INSERT with as many HOST parameters as possible + let currentChunkIndex = 0; + request.insert.forEach((value, key) => { + let keyValueChunk = keysValuesChunks[currentChunkIndex]; + + if (keyValueChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyValueChunk = []; + keysValuesChunks.push(keyValueChunk); + } + + keyValueChunk.push(key, value); + }); + + keysValuesChunks.forEach(keysValuesChunk => { + this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => { + const keys: string[] = []; + let length = 0; + request.insert!.forEach((value, key) => { + keys.push(key); + length += value.length; + }); + + return `Keys: ${keys.join(', ')} Length: ${length}`; + }); }); } + // DELETE if (request.delete && request.delete.size) { - this.prepare(connection, 'DELETE FROM ItemTable WHERE key=?', stmt => { - request.delete!.forEach(key => { - stmt.run(key); - }); - }, () => { - const keys: string[] = []; - request.delete!.forEach(key => { - keys.push(key); - }); + const keysChunks: (string[])[] = []; + keysChunks.push([]); // seed with initial empty chunk - return `Keys: ${keys.join(', ')}`; + // Split keys into chunks of SQLiteStorageDatabase.MAX_HOST_PARAMETERS + // so that we can efficiently run the DELETE with as many HOST parameters + // as possible + let currentChunkIndex = 0; + request.delete.forEach(key => { + let keyChunk = keysChunks[currentChunkIndex]; + + if (keyChunk.length > SQLiteStorageDatabase.MAX_HOST_PARAMETERS) { + currentChunkIndex++; + keyChunk = []; + keysChunks.push(keyChunk); + } + + keyChunk.push(key); + }); + + keysChunks.forEach(keysChunk => { + this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => { + const keys: string[] = []; + request.delete!.forEach(key => { + keys.push(key); + }); + + return `Keys: ${keys.join(', ')}`; + }); }); } }); diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index b999d91e2c4..ceaea708546 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -32,8 +32,16 @@ suite('Arrays', () => { }); test('stableSort', () => { + function fill(num: number, valueFn: () => T, arr: T[] = []): T[] { + for (let i = 0; i < num; i++) { + arr[i] = valueFn(); + } + + return arr; + } + let counter = 0; - let data = arrays.fill(10000, () => ({ n: 1, m: counter++ })); + let data = fill(10000, () => ({ n: 1, m: counter++ })); arrays.mergeSort(data, (a, b) => a.n - b.n); diff --git a/src/vs/base/test/node/storage/storage.test.ts b/src/vs/base/test/node/storage/storage.test.ts index 9d4b5085429..215c2bb530a 100644 --- a/src/vs/base/test/node/storage/storage.test.ts +++ b/src/vs/base/test/node/storage/storage.test.ts @@ -737,4 +737,68 @@ suite('SQLite Storage Library', () => { await del(storageDir, tmpdir()); }); + + test('lots of INSERT & DELETE (below inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 200; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); + + test('lots of INSERT & DELETE (above inline max)', async () => { + const storageDir = uniqueStorageDir(); + + await mkdirp(storageDir); + + const storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); + + const items = new Map(); + const keys: Set = new Set(); + for (let i = 0; i < 400; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + equal(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + equal(storedItems.size, 0); + + await storage.close(); + + await del(storageDir, tmpdir()); + }); }); From 89e86a5a152542afdf05698ab6d912e73864a584 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 11:45:51 +0100 Subject: [PATCH 0424/1066] remove border --- src/vs/base/browser/ui/list/list.css | 2 -- src/vs/base/browser/ui/list/listWidget.ts | 2 +- src/vs/platform/theme/common/styler.ts | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 171d2ff73a3..2b312c31ee1 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -69,8 +69,6 @@ display: flex; position: absolute; border-radius: 2px; - border-width: 1px; - border-style: solid; padding: 0px 3px; max-width: calc(100% - 10px); text-overflow: ellipsis; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index df91bef1758..5a35edc6d91 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -730,7 +730,7 @@ export class DefaultStyleController implements IStyleController { } if (styles.listMatchesOutline) { - content.push(`.monaco-list-type-filter { border-color: ${styles.listMatchesOutline}; }`); + content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listMatchesOutline}; }`); } if (styles.listMatchesShadow) { diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index 3b8f646d040..f14f28c3f5c 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -249,7 +249,6 @@ export const defaultListStyles: IColorMapping = { listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, listMatchesBackground: blend2(editorFindMatchHighlight, editorWidgetBackground), - listMatchesOutline: focusBorder, listMatchesShadow: widgetShadow }; From cfd5d358efa49c9dd3383bb8863f56a4c9997e5c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Jan 2019 11:56:15 +0100 Subject: [PATCH 0425/1066] fix #66753 --- .../workbench/parts/surveys/electron-browser/nps.contribution.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts index ec4e7e5ec44..1db2a5bfdbb 100644 --- a/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts +++ b/src/vs/workbench/parts/surveys/electron-browser/nps.contribution.ts @@ -74,7 +74,6 @@ class NPSContribution implements IWorkbenchContribution { run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL) }, { label: nls.localize('neverAgain', "Don't Show Again"), - isSecondary: true, run: () => { storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); From 49f9e1012149291bf98303823b8836dee3edabac Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 21 Jan 2019 12:25:29 +0100 Subject: [PATCH 0426/1066] file commands: fully remove dependency on old Tree --- .../files/electron-browser/fileCommands.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index 41aea5e0b17..b45e20aed65 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -17,7 +17,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { IListService } from 'vs/platform/list/browser/listService'; -import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IResourceInput } from 'vs/platform/editor/common/editor'; @@ -260,14 +259,8 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const editorService = accessor.get(IEditorService); const listService = accessor.get(IListService); const fileService = accessor.get(IFileService); - const tree = listService.lastFocusedList; const resources = getMultiSelectedResources(resource, listService, editorService); - // Remove highlight - if (tree instanceof Tree) { - tree.clearHighlight(); - } - // Set side input if (resources.length) { return fileService.resolveFiles(resources.map(resource => ({ resource }))).then(resolved => { @@ -318,12 +311,6 @@ CommandsRegistry.registerCommand({ id: SELECT_FOR_COMPARE_COMMAND_ID, handler: (accessor, resource: URI | object) => { const listService = accessor.get(IListService); - const tree = listService.lastFocusedList; - // Remove highlight - if (tree instanceof Tree) { - tree.clearHighlight(); - tree.domFocus(); - } globalResourceToCompare = getResourceForCommand(resource, listService, accessor.get(IEditorService)); if (!resourceSelectedForCompareContext) { @@ -355,12 +342,6 @@ CommandsRegistry.registerCommand({ handler: (accessor, resource: URI | object) => { const editorService = accessor.get(IEditorService); const listService = accessor.get(IListService); - const tree = listService.lastFocusedList; - - // Remove highlight - if (tree instanceof Tree) { - tree.clearHighlight(); - } return editorService.openEditor({ leftResource: globalResourceToCompare, From fa6ac99972697894dc72ab21f64ee7b84af4b61e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 12:35:53 +0100 Subject: [PATCH 0427/1066] introduce workbench.list.keyboardNavigation --- src/vs/base/browser/ui/list/listWidget.ts | 90 +++++++++--- src/vs/base/browser/ui/tree/abstractTree.ts | 150 +++++++++++++------- src/vs/platform/list/browser/listService.ts | 53 ++++++- 3 files changed, 215 insertions(+), 78 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 5a35edc6d91..3620125f0aa 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -336,7 +336,9 @@ export function mightProducePrintableCharacter(event: IKeyboardEvent): boolean { class TypeLabelController implements IDisposable { + private enabled = false; private state: TypeLabelControllerState = TypeLabelControllerState.Idle; + private enabledDisposables: IDisposable[] = []; private disposables: IDisposable[] = []; constructor( @@ -344,17 +346,45 @@ class TypeLabelController implements IDisposable { private view: ListView, private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider ) { - const onChar = Event.chain(domEvent(view.domNode, 'keydown')) + list.onDidUpdateOptions(this.onDidUpdateListOptions, this, this.disposables); + this.onDidUpdateListOptions(list.options); + } + + private onDidUpdateListOptions(options: IListOptions): void { + if (options.enableKeyboardNavigation) { + this.enable(); + } else { + this.disable(); + } + } + + private enable(): void { + if (this.enabled) { + return; + } + + const onChar = Event.chain(domEvent(this.view.domNode, 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement)) .map(event => new StandardKeyboardEvent(event)) - .filter(keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e)) + .filter(this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? e => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : e => mightProducePrintableCharacter(e)) .map(event => event.browserEvent.key) .event; const onClear = Event.debounce(onChar, () => null, 800); const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i)); - onInput(this.onInput, this, this.disposables); + onInput(this.onInput, this, this.enabledDisposables); + + this.enabled = true; + } + + private disable(): void { + if (!this.enabled) { + return; + } + + this.enabledDisposables = dispose(this.enabledDisposables); + this.enabled = false; } private onInput(word: string | null): void { @@ -381,6 +411,7 @@ class TypeLabelController implements IDisposable { } dispose() { + this.disable(); this.disposables = dispose(this.disposables); } } @@ -747,6 +778,7 @@ export class DefaultStyleController implements IStyleController { export interface IListOptions extends IListStyles { readonly identityProvider?: IIdentityProvider; readonly dnd?: IListDragAndDrop; + readonly enableKeyboardNavigation?: boolean; readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly ariaLabel?: string; readonly keyboardSupport?: boolean; @@ -1004,6 +1036,10 @@ class ListViewDragAndDrop implements IListViewDragAndDrop { } } +export interface IListOptionsUpdate { + readonly enableKeyboardNavigation?: boolean; +} + export class List implements ISpliceable, IDisposable { private static InstanceCount = 0; @@ -1017,6 +1053,9 @@ export class List implements ISpliceable, IDisposable { private styleElement: HTMLStyleElement; private styleController: IStyleController; + private _onDidUpdateOptions = new Emitter>(); + readonly onDidUpdateOptions = this._onDidUpdateOptions.event; + protected disposables: IDisposable[]; @memoize get onFocusChange(): Event> { @@ -1098,24 +1137,24 @@ export class List implements ISpliceable, IDisposable { container: HTMLElement, virtualDelegate: IListVirtualDelegate, renderers: IListRenderer[], - options: IListOptions = DefaultOptions + private _options: IListOptions = DefaultOptions ) { this.focus = new FocusTrait(i => this.getElementDomId(i)); this.selection = new Trait('selected'); - mixin(options, defaultStyles, false); + mixin(_options, defaultStyles, false); const baseRenderers: IListRenderer[] = [this.focus.renderer, this.selection.renderer]; - if (options.accessibilityProvider) { - baseRenderers.push(new AccessibiltyRenderer(options.accessibilityProvider)); + if (_options.accessibilityProvider) { + baseRenderers.push(new AccessibiltyRenderer(_options.accessibilityProvider)); } renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r])); const viewOptions: IListViewOptions = { - ...options, - dnd: options.dnd && new ListViewDragAndDrop(this, options.dnd) + ..._options, + dnd: _options.dnd && new ListViewDragAndDrop(this, _options.dnd) }; this.view = new ListView(container, virtualDelegate, renderers, viewOptions); @@ -1125,11 +1164,11 @@ export class List implements ISpliceable, IDisposable { this.styleElement = DOM.createStyleSheet(this.view.domNode); - this.styleController = options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix); + this.styleController = _options.styleController || new DefaultStyleController(this.styleElement, this.idPrefix); this.spliceable = new CombinedSpliceable([ - new TraitSpliceable(this.focus, this.view, options.identityProvider), - new TraitSpliceable(this.selection, this.view, options.identityProvider), + new TraitSpliceable(this.focus, this.view, _options.identityProvider), + new TraitSpliceable(this.selection, this.view, _options.identityProvider), this.view ]); @@ -1140,28 +1179,37 @@ export class List implements ISpliceable, IDisposable { this.disposables.push(new DOMFocusController(this, this.view)); - if (typeof options.keyboardSupport !== 'boolean' || options.keyboardSupport) { - const controller = new KeyboardController(this, this.view, options); + if (typeof _options.keyboardSupport !== 'boolean' || _options.keyboardSupport) { + const controller = new KeyboardController(this, this.view, _options); this.disposables.push(controller); } - if (options.keyboardNavigationLabelProvider) { - const controller = new TypeLabelController(this, this.view, options.keyboardNavigationLabelProvider); + if (_options.keyboardNavigationLabelProvider) { + const controller = new TypeLabelController(this, this.view, _options.keyboardNavigationLabelProvider); this.disposables.push(controller); } - if (typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true) { - this.disposables.push(new MouseController(this, this.view, options)); + if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) { + this.disposables.push(new MouseController(this, this.view, _options)); } this.onFocusChange(this._onFocusChange, this, this.disposables); this.onSelectionChange(this._onSelectionChange, this, this.disposables); - if (options.ariaLabel) { - this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", options.ariaLabel)); + if (_options.ariaLabel) { + this.view.domNode.setAttribute('aria-label', localize('aria list', "{0}. Use the navigation keys to navigate.", _options.ariaLabel)); } - this.style(options); + this.style(_options); + } + + updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + this._onDidUpdateOptions.fire(this._options); + } + + get options(): IListOptions { + return this._options; } splice(start: number, deleteCount: number, elements: T[] = []): void { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 6d9db831981..bb87413701e 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -140,7 +140,13 @@ function asListOptions(modelProvider: () => ITreeModel implements ITreeFilter { filter(element: T, parentVisibility: TreeVisibility): TreeFilterResult { if (this._filter) { const result = this._filter.filter(element, parentVisibility); + + if (this.tree.options.simpleKeyboardNavigation) { + return result; + } + let visibility: TreeVisibility; if (typeof result === 'boolean') { @@ -302,7 +313,7 @@ class TypeFilter implements ITreeFilter { } } - if (!this._pattern) { + if (this.tree.options.simpleKeyboardNavigation || !this._pattern) { return { data: FuzzyScore.Default, visibility: true }; } @@ -310,7 +321,7 @@ class TypeFilter implements ITreeFilter { const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, label, label.toLowerCase(), 0, true); if (!score) { - if (this.tree.configuration.filterOnType) { + if (this.tree.options.filterOnType) { return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } else { return { data: FuzzyScore.Default, visibility: true }; @@ -333,47 +344,70 @@ class TypeFilterController implements IDisposable { private _onDidChangePattern = new Emitter(); readonly onDidChangePattern = this._onDidChangePattern.event; + private enabled = false; private positionClassName = 'ne'; private domNode: HTMLElement; - private label: HTMLElement; - private filterOnType: HTMLInputElement; + private labelDomNode: HTMLElement; + private filterOnTypeDomNode: HTMLInputElement; + private clearDomNode: HTMLElement; private _pattern = ''; + private enabledDisposables: IDisposable[] = []; private disposables: IDisposable[] = []; constructor( private tree: AbstractTree, private view: List>, private filter: TypeFilter, - keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider + private keyboardNavigationLabelProvider: IKeyboardNavigationLabelProvider ) { this.domNode = $(`.monaco-list-type-filter.${this.positionClassName}`); this.domNode.draggable = true; domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); - this.label = append(this.domNode, $('span.label')); + this.labelDomNode = append(this.domNode, $('span.label')); const controls = append(this.domNode, $('.controls')); - this.filterOnType = append(controls, $('input.filter')); - this.filterOnType.type = 'checkbox'; - this.filterOnType.checked = !!tree.configuration.filterOnType; - this.filterOnType.tabIndex = -1; + this.filterOnTypeDomNode = append(controls, $('input.filter')); + this.filterOnTypeDomNode.type = 'checkbox'; + this.filterOnTypeDomNode.checked = !!tree.options.filterOnType; + this.filterOnTypeDomNode.tabIndex = -1; this.updateFilterOnTypeTitle(); - domEvent(this.filterOnType, 'input')(this.onDidChangeFilterOnType, this, this.disposables); + domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); - const clear = append(controls, $('button.clear')); - clear.tabIndex = -1; - clear.title = localize('clear', "Clear"); - const onClear = domEvent(clear, 'click'); + this.clearDomNode = append(controls, $('button.clear')); + this.clearDomNode.tabIndex = -1; + this.clearDomNode.title = localize('clear', "Clear"); - const isPrintableCharEvent = keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); - const onKeyDown = Event.chain(domEvent(view.getHTMLElement(), 'keydown')) - .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnType) + tree.onDidUpdateOptions(this.onDidUpdateTreeOptions, this, this.disposables); + this.onDidUpdateTreeOptions(tree.options); + } + + private onDidUpdateTreeOptions(options: IAbstractTreeOptions): void { + if (options.simpleKeyboardNavigation) { + this.disable(); + } else { + this.enable(); + } + + this.filterOnTypeDomNode.checked = !!options.filterOnType; + this.tree.refilter(); + } + + private enable(): void { + if (this.enabled) { + return; + } + + const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); + 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)) .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) { @@ -388,7 +422,20 @@ class TypeFilterController implements IDisposable { }, '') .event; - onInput(this.onInput, this, this.disposables); + onInput(this.onInput, this, this.enabledDisposables); + this.tree.refilter(); + this.enabled = true; + } + + private disable(): void { + if (!this.enabled) { + return; + } + + this.domNode.remove(); + this.enabledDisposables = dispose(this.enabledDisposables); + this.tree.refilter(); + this.enabled = false; } private onInput(pattern: string): void { @@ -401,7 +448,7 @@ class TypeFilterController implements IDisposable { this.tree.domFocus(); } - this.label.textContent = pattern.length > 16 + this.labelDomNode.textContent = pattern.length > 16 ? '…' + pattern.substr(pattern.length - 16) : pattern; @@ -485,20 +532,22 @@ class TypeFilterController implements IDisposable { } private onDidChangeFilterOnType(): void { - this.tree.updateConfiguration({ filterOnType: this.filterOnType.checked }); + this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); + this.tree.refilter(); this.tree.domFocus(); this.updateFilterOnTypeTitle(); } private updateFilterOnTypeTitle(): void { - if (this.filterOnType.checked) { - this.filterOnType.title = localize('disable filter on type', "Disable Filter on Type"); + if (this.filterOnTypeDomNode.checked) { + this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); } else { - this.filterOnType.title = localize('enable filter on type', "Enable Filter on Type"); + this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); } } dispose() { + this.disable(); this.disposables = dispose(this.disposables); } } @@ -530,6 +579,7 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IAbstractTr readonly autoExpandSingleChildren?: boolean; } -export interface IAbstractTreeConfiguration { - readonly filterOnType: boolean; -} - export abstract class AbstractTree implements IDisposable { private view: List>; private renderers: TreeRenderer[]; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; - private _configuration: IAbstractTreeConfiguration; protected model: ITreeModel; protected disposables: IDisposable[] = []; + private _onDidUpdateOptions = new Emitter>(); + readonly onDidUpdateOptions = this._onDidUpdateOptions.event; + 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); } @@ -577,33 +625,29 @@ export abstract class AbstractTree implements IDisposable container: HTMLElement, delegate: IListVirtualDelegate, renderers: ITreeRenderer[], - options: IAbstractTreeOptions = {} + private _options: IAbstractTreeOptions = {} ) { const treeDelegate = new ComposedTreeDelegate>(delegate); const onDidChangeCollapseStateRelay = new Relay>(); - this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, options)); + this.renderers = renderers.map(r => new TreeRenderer(r, onDidChangeCollapseStateRelay.event, _options)); this.disposables.push(...this.renderers); - this._configuration = { - filterOnType: typeof options.filterOnType === 'boolean' ? options.filterOnType : false - }; - let filter: ITreeFilter | undefined; - if (options.keyboardNavigationLabelProvider) { - filter = new TypeFilter(this, options.keyboardNavigationLabelProvider, options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here - options = { ...options, filter }; + if (_options.keyboardNavigationLabelProvider) { + filter = new TypeFilter(this, _options.keyboardNavigationLabelProvider, _options.filter as ITreeFilter) as ITreeFilter; // TODO need typescript help here + _options = { ..._options, filter }; } - this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, options)); + this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, _options)); - this.model = this.createModel(this.view, options); + this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); - if (options.keyboardSupport !== false) { + if (_options.keyboardSupport !== false) { const onKeyDown = Event.chain(this.view.onKeyDown) .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)); @@ -613,8 +657,8 @@ export abstract class AbstractTree implements IDisposable onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } - if (options.keyboardNavigationLabelProvider) { - const typeFilterController = new TypeFilterController(this, this.view, filter as TypeFilter, options.keyboardNavigationLabelProvider); + if (_options.keyboardNavigationLabelProvider) { + const typeFilterController = new TypeFilterController(this, 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); @@ -622,19 +666,19 @@ export abstract class AbstractTree implements IDisposable } } - updateOptions(options: IAbstractTreeOptionsUpdate = {}): void { + updateOptions(optionsUpdate: IAbstractTreeOptionsUpdate = {}): void { + this._options = { ...this._options, ...optionsUpdate }; + for (const renderer of this.renderers) { - renderer.updateOptions(options); + renderer.updateOptions(optionsUpdate); } + + this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation }); + this._onDidUpdateOptions.fire(this._options); } - updateConfiguration(update: Partial): void { - this._configuration = { ...this._configuration, ...update }; - this.refilter(); - } - - get configuration(): IAbstractTreeConfiguration { - return this._configuration; + get options(): IAbstractTreeOptions { + return this._options; } // Widget diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 4d7be7cd635..abf900f8577 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -113,6 +113,7 @@ function createScopedContextKeyService(contextKeyService: IContextKeyService, wi export const multiSelectModifierSettingKey = 'workbench.list.multiSelectModifier'; export const openModeSettingKey = 'workbench.list.openMode'; export const horizontalScrollingKey = 'workbench.tree.horizontalScrolling'; +export const keyboardNavigationSettingKey = 'workbench.list.keyboardNavigation'; const treeIndentKey = 'workbench.tree.indent'; function useAltAsMultipleSelectionModifier(configurationService: IConfigurationService): boolean { @@ -905,12 +906,16 @@ export class WorkbenchObjectTree, TFilterData = void> @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService), - indent: configurationService.getValue(treeIndentKey) + indent: configurationService.getValue(treeIndentKey), + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -950,6 +955,13 @@ export class WorkbenchObjectTree, TFilterData = void> const indent = configurationService.getValue(treeIndentKey); this.updateOptions({ indent }); } + if (e.affectsConfiguration(keyboardNavigationSettingKey)) { + const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + this.updateOptions({ + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' + }); + } }) ); } @@ -986,12 +998,16 @@ export class WorkbenchDataTree extends DataTree(keyboardNavigationSettingKey); + super(container, delegate, renderers, dataSource, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService), - indent: configurationService.getValue(treeIndentKey) + indent: configurationService.getValue(treeIndentKey), + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -1031,6 +1047,13 @@ export class WorkbenchDataTree extends DataTree(treeIndentKey); this.updateOptions({ indent }); } + if (e.affectsConfiguration(keyboardNavigationSettingKey)) { + const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + this.updateOptions({ + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' + }); + } }) ); } @@ -1062,12 +1085,16 @@ export class WorkbenchAsyncDataTree extends Async @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + super(container, delegate, renderers, dataSource, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService), - indent: configurationService.getValue(treeIndentKey) + indent: configurationService.getValue(treeIndentKey), + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -1107,6 +1134,13 @@ export class WorkbenchAsyncDataTree extends Async const indent = configurationService.getValue(treeIndentKey); this.updateOptions({ indent }); } + if (e.affectsConfiguration(keyboardNavigationSettingKey)) { + const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); + this.updateOptions({ + simpleKeyboardNavigation: keyboardNavigation === 'simple', + filterOnType: keyboardNavigation === 'filter' + }); + } }) ); } @@ -1158,6 +1192,17 @@ configurationRegistry.registerConfiguration({ 'type': 'number', 'default': 8, 'description': localize('tree indent setting', "Controls tree indentation in pixels.") - } + }, + [keyboardNavigationSettingKey]: { + '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.") + ], + 'default': 'highlight', + 'description': localize('keyboardNavigationSettingKey', "Controls the keyboard navigation style for lists and trees.") + }, } }); From d5f8c2ad1d221514ac6f03c360dce772ff7d7500 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 13:49:46 +0100 Subject: [PATCH 0428/1066] tree: reset filter pattern --- src/vs/base/browser/ui/tree/abstractTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index bb87413701e..0987c65b902 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -423,6 +423,7 @@ class TypeFilterController implements IDisposable { .event; onInput(this.onInput, this, this.enabledDisposables); + this.filter.pattern = ''; this.tree.refilter(); this.enabled = true; } From 4df85d2843845c184e27aa15f4815f5df6920559 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 15:20:30 +0100 Subject: [PATCH 0429/1066] fix filter lingering around --- src/vs/base/browser/ui/tree/abstractTree.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 0987c65b902..3fef9505960 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -322,13 +322,13 @@ class TypeFilter implements ITreeFilter { if (!score) { if (this.tree.options.filterOnType) { - return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; + return TreeVisibility.Recurse; } else { return { data: FuzzyScore.Default, visibility: true }; } - // DEMO: filter - // return TreeVisibility.Recurse; + // DEMO: smarter filter ? + // return parentVisibility === TreeVisibility.Visible ? true : TreeVisibility.Recurse; } return { data: score, visibility: true }; From 306feb428aaaca054afb97b80360972a00f71bd8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 21 Jan 2019 15:45:52 +0100 Subject: [PATCH 0430/1066] filter empty affordance --- src/vs/base/browser/ui/list/list.css | 18 ++++++++++++++++ src/vs/base/browser/ui/tree/abstractTree.ts | 23 +++++++++++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 2b312c31ee1..0466517aa67 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -168,4 +168,22 @@ .hc-black .monaco-list-type-filter > .controls > .clear { background-image: url("media/close-hc.svg"); +} + +.monaco-list-type-filter-message { + position: absolute; + box-sizing: border-box; + width: 100%; + height: 100%; + top: 0; + left: 0; + padding: 40px 1em 1em 1em; + text-align: center; + white-space: normal; + opacity: 0.7; + pointer-events: none; +} + +.monaco-list-type-filter-message:empty { + display: none; } \ No newline at end of file diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3fef9505960..1bc2f89ad9b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -341,12 +341,10 @@ class TypeFilterController implements IDisposable { return this._pattern; } - private _onDidChangePattern = new Emitter(); - readonly onDidChangePattern = this._onDidChangePattern.event; - private enabled = false; private positionClassName = 'ne'; private domNode: HTMLElement; + private messageDomNode: HTMLElement; private labelDomNode: HTMLElement; private filterOnTypeDomNode: HTMLInputElement; private clearDomNode: HTMLElement; @@ -365,6 +363,8 @@ class TypeFilterController implements IDisposable { this.domNode.draggable = true; domEvent(this.domNode, 'dragstart')(this.onDragStart, this, this.disposables); + this.messageDomNode = append(view.getHTMLElement(), $(`.monaco-list-type-filter-message`)); + this.labelDomNode = append(this.domNode, $('span.label')); const controls = append(this.domNode, $('.controls')); @@ -392,6 +392,7 @@ class TypeFilterController implements IDisposable { this.filterOnTypeDomNode.checked = !!options.filterOnType; this.tree.refilter(); + this.updateMessage(); } private enable(): void { @@ -425,6 +426,7 @@ class TypeFilterController implements IDisposable { onInput(this.onInput, this, this.enabledDisposables); this.filter.pattern = ''; this.tree.refilter(); + this.updateMessage(); this.enabled = true; } @@ -436,6 +438,7 @@ class TypeFilterController implements IDisposable { this.domNode.remove(); this.enabledDisposables = dispose(this.enabledDisposables); this.tree.refilter(); + this.updateMessage(); this.enabled = false; } @@ -456,8 +459,9 @@ class TypeFilterController implements IDisposable { this._pattern = pattern; this.filter.pattern = pattern; this.tree.refilter(); + this.tree.focusNext(0, true); - this._onDidChangePattern.fire(pattern); + this.updateMessage(); } private onDragStart(): void { @@ -536,6 +540,7 @@ class TypeFilterController implements IDisposable { this.tree.updateOptions({ filterOnType: this.filterOnTypeDomNode.checked }); this.tree.refilter(); this.tree.domFocus(); + this.updateMessage(); this.updateFilterOnTypeTitle(); } @@ -547,6 +552,14 @@ class TypeFilterController implements IDisposable { } } + private updateMessage(): void { + if (this.pattern && this.view.length === 0) { + this.messageDomNode.textContent = localize('empty', "No elements found"); + } else { + this.messageDomNode.innerHTML = ''; + } + } + dispose() { this.disable(); this.disposables = dispose(this.disposables); @@ -662,8 +675,6 @@ export abstract class AbstractTree implements IDisposable const typeFilterController = new TypeFilterController(this, 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); - - typeFilterController.onDidChangePattern(() => this.focusNext(0, true), this, this.disposables); } } From 642209ab22f4746f620e7db1ebf7352af3743848 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Jan 2019 15:48:12 +0100 Subject: [PATCH 0431/1066] try to fix #66583 --- src/vs/code/electron-main/window.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index ff56a680870..d72458428f7 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -25,6 +25,7 @@ import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { getBackgroundColor } from 'vs/code/electron-main/theme'; import { IStorageMainService } from 'vs/platform/storage/node/storageMainService'; +import { RunOnceScheduler } from 'vs/base/common/async'; export interface IWindowCreationOptions { state: IWindowState; @@ -360,17 +361,31 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._lastFocusTime = Date.now(); }); - // Simple fullscreen doesn't resize automatically when the resolution changes + // Simple fullscreen doesn't resize automatically when the resolution changes so as a workaround + // we need to detect when display metrics change or displays are added/removed and toggle the + // fullscreen manually. if (isMacintosh) { - const displayMetricsChangedListener = () => { - if (this.isFullScreen() && !this.useNativeFullScreen()) { + const simpleFullScreenScheduler = this._register(new RunOnceScheduler(() => { + if (!this._win) { + return; // disposed + } + + if (!this.useNativeFullScreen() && this.isFullScreen()) { this.setFullScreen(false); this.setFullScreen(true); } - }; + }, 100)); - screen.addListener('display-metrics-changed', displayMetricsChangedListener); - this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayMetricsChangedListener))); + const displayChangedListener = () => simpleFullScreenScheduler.schedule(); + + screen.on('display-metrics-changed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-metrics-changed', displayChangedListener))); + + screen.on('display-added', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-added', displayChangedListener))); + + screen.on('display-removed', displayChangedListener); + this._register(toDisposable(() => screen.removeListener('display-removed', displayChangedListener))); } // Window (Un)Maximize From d3674dfe5288b8ebe11c7c83b2fdda79edcdf03d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Jan 2019 16:20:19 +0100 Subject: [PATCH 0432/1066] enable watching only with hidden flag and for extension development --- .../services/themes/common/workbenchThemeService.ts | 1 + .../services/themes/electron-browser/colorThemeData.ts | 9 +++++++-- .../themes/electron-browser/workbenchThemeService.ts | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 97b02993975..e9a7707972d 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -100,4 +100,5 @@ export interface IThemeExtensionPoint { label?: string; description?: string; path: string; + _watch: boolean; // unsupported options to watch location } \ No newline at end of file diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index 823f339d164..36fb0e8f096 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -43,6 +43,7 @@ export class ColorThemeData implements IColorTheme { description?: string; isLoaded: boolean; location?: URI; + watch: boolean; extensionData: ExtensionData; get tokenColors(): ITokenColorizationRule[] { @@ -185,7 +186,8 @@ export class ColorThemeData implements IColorTheme { selector: this.id.split(' ').join('.'), // to not break old clients themeTokenColors: this.themeTokenColors, extensionData: this.extensionData, - colorMap: colorMapData + colorMap: colorMapData, + watch: this.watch }); } @@ -214,6 +216,7 @@ export class ColorThemeData implements IColorTheme { themeData.settingsId = null; themeData.isLoaded = false; themeData.themeTokenColors = [{ settings: {} }]; + themeData.watch = false; return themeData; } @@ -224,6 +227,7 @@ export class ColorThemeData implements IColorTheme { themeData.settingsId = settingsId; themeData.isLoaded = true; themeData.themeTokenColors = [{ settings: {} }]; + themeData.watch = false; return themeData; } @@ -240,7 +244,7 @@ export class ColorThemeData implements IColorTheme { } break; case 'themeTokenColors': - case 'id': case 'label': case 'settingsId': case 'extensionData': + case 'id': case 'label': case 'settingsId': case 'extensionData': case 'watch': theme[key] = data[key]; break; } @@ -260,6 +264,7 @@ export class ColorThemeData implements IColorTheme { themeData.label = theme.label || Paths.basename(theme.path); themeData.settingsId = theme.id || themeData.label; themeData.description = theme.description; + themeData.watch = theme._watch === true; themeData.location = colorThemeLocation; themeData.extensionData = extensionData; themeData.isLoaded = false; diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index aa2ad1e5ca0..37a34ee5ff8 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -356,8 +356,8 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.watchedColorThemeLocation) { this.fileService.unwatchFileChanges(this.watchedColorThemeLocation); } - this.watchedColorThemeLocation = newTheme.location; - if (this.watchedColorThemeLocation) { + if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { + this.watchedColorThemeLocation = newTheme.location; this.fileService.watchFileChanges(this.watchedColorThemeLocation); } } From afee6f241d3558ebe3cc21a07ad2b3abd43948d6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 21 Jan 2019 17:13:06 +0100 Subject: [PATCH 0433/1066] Fix #66854 --- .../common/configurationRegistry.ts | 157 +++++++++++++----- .../node/configurationService.ts | 4 +- .../common/configurationExtensionPoint.ts | 83 ++++++--- .../node/configurationService.ts | 2 +- 4 files changed, 179 insertions(+), 67 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 8a6f52fbc68..981fe710fd8 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -26,7 +26,22 @@ export interface IConfigurationRegistry { /** * Register multiple configurations to the registry. */ - registerConfigurations(configurations: IConfigurationNode[], defaultConfigurations: IDefaultConfigurationExtension[], validate?: boolean): void; + registerConfigurations(configurations: IConfigurationNode[], validate?: boolean): void; + + /** + * Deregister multiple configurations from the registry. + */ + deregisterConfigurations(configurations: IConfigurationNode[]): void; + + /** + * Register multiple default configurations to the registry. + */ + registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void; + + /** + * Deregister multiple default configurations from the registry. + */ + deregisterDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void; /** * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values. @@ -44,7 +59,7 @@ export interface IConfigurationRegistry { * Event that fires whenver a configuration has been * registered. */ - onDidRegisterConfiguration: Event; + onDidUpdateConfiguration: Event; /** * Returns all configuration nodes contributed to this registry. @@ -109,21 +124,27 @@ const contributionRegistry = Registry.as(JSONExtensio class ConfigurationRegistry implements IConfigurationRegistry { - private configurationContributors: IConfigurationNode[]; - private configurationProperties: { [qualifiedKey: string]: IJSONSchema }; - private excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema }; - private editorConfigurationSchema: IJSONSchema; - private overrideIdentifiers: string[] = []; + private readonly defaultOverridesConfigurationNode: IConfigurationNode; + private readonly configurationContributors: IConfigurationNode[]; + private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema }; + private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema }; + private readonly editorConfigurationSchema: IJSONSchema; + private readonly overrideIdentifiers: string[] = []; private overridePropertyPattern: string; private readonly _onDidSchemaChange = new Emitter(); readonly onDidSchemaChange: Event = this._onDidSchemaChange.event; - private readonly _onDidRegisterConfiguration: Emitter = new Emitter(); - readonly onDidRegisterConfiguration: Event = this._onDidRegisterConfiguration.event; + private readonly _onDidUpdateConfiguration: Emitter = new Emitter(); + readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; constructor() { - this.configurationContributors = []; + this.defaultOverridesConfigurationNode = { + id: 'defaultOverrides', + title: nls.localize('defaultConfigurations.title', "Default Configuration Overrides"), + properties: {} + }; + this.configurationContributors = [this.defaultOverridesConfigurationNode]; this.editorConfigurationSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting' }; this.configurationProperties = {}; this.excludedConfigurationProperties = {}; @@ -133,15 +154,10 @@ class ConfigurationRegistry implements IConfigurationRegistry { } public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void { - this.registerConfigurations([configuration], [], validate); + this.registerConfigurations([configuration], validate); } - public registerConfigurations(configurations: IConfigurationNode[], defaultConfigurations: IDefaultConfigurationExtension[], validate: boolean = true): void { - const configurationNode = this.toConfiguration(defaultConfigurations); - if (configurationNode) { - configurations.push(configurationNode); - } - + public registerConfigurations(configurations: IConfigurationNode[], validate: boolean = true): void { const properties: string[] = []; configurations.forEach(configuration => { properties.push(...this.validateAndRegisterProperties(configuration, validate)); // fills in defaults @@ -150,7 +166,89 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.updateSchemaForOverrideSettingsConfiguration(configuration); }); - this._onDidRegisterConfiguration.fire(properties); + this._onDidSchemaChange.fire(); + this._onDidUpdateConfiguration.fire(properties); + } + + public deregisterConfigurations(configurations: IConfigurationNode[]): void { + const properties: string[] = []; + const deregisterConfiguration = (configuration: IConfigurationNode) => { + if (configuration.properties) { + for (const key in configuration.properties) { + properties.push(key); + + delete this.configurationProperties[key]; + delete this.editorConfigurationSchema.properties![key]; + + // Delete from schema + delete allSettings.properties[key]; + switch (configuration.properties[key].scope) { + case ConfigurationScope.APPLICATION: + delete applicationSettings.properties[key]; + break; + case ConfigurationScope.WINDOW: + delete windowSettings.properties[key]; + break; + case ConfigurationScope.RESOURCE: + delete resourceSettings.properties[key]; + break; + } + } + } + if (configuration.allOf) { + configuration.allOf.forEach(node => deregisterConfiguration(node)); + } + }; + for (const configuration of configurations) { + deregisterConfiguration(configuration); + const index = this.configurationContributors.indexOf(configuration); + if (index !== -1) { + this.configurationContributors.splice(index, 1); + } + } + + contributionRegistry.registerSchema(editorConfigurationSchemaId, this.editorConfigurationSchema); + this._onDidSchemaChange.fire(); + this._onDidUpdateConfiguration.fire(properties); + } + + public registerDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void { + const properties: string[] = []; + + for (const defaultConfiguration of defaultConfigurations) { + for (const key in defaultConfiguration.defaults) { + const defaultValue = defaultConfiguration.defaults[key]; + if (OVERRIDE_PROPERTY_PATTERN.test(key) && typeof defaultValue === 'object') { + const propertySchema: IConfigurationPropertySchema = { + type: 'object', + default: defaultValue, + description: nls.localize('overrideSettings.description', "Configure editor settings to be overridden for {0} language.", key), + $ref: editorConfigurationSchemaId + }; + allSettings.properties[key] = propertySchema; + this.defaultOverridesConfigurationNode.properties![key] = propertySchema; + this.configurationProperties[key] = propertySchema; + properties.push(key); + } + } + } + + this._onDidSchemaChange.fire(); + this._onDidUpdateConfiguration.fire(properties); + } + + public deregisterDefaultConfigurations(defaultConfigurations: IDefaultConfigurationExtension[]): void { + const properties: string[] = []; + for (const defaultConfiguration of defaultConfigurations) { + for (const key in defaultConfiguration.defaults) { + properties.push(key); + delete allSettings.properties[key]; + delete this.defaultOverridesConfigurationNode.properties![key]; + delete this.configurationProperties[key]; + } + } + this._onDidSchemaChange.fire(); + this._onDidUpdateConfiguration.fire(properties); } public notifyConfigurationSchemaUpdated(configuration: IConfigurationNode) { @@ -162,28 +260,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.updateOverridePropertyPatternKey(); } - private toConfiguration(defaultConfigurations: IDefaultConfigurationExtension[]): IConfigurationNode | null { - const configurationNode: IConfigurationNode = { - id: 'defaultOverrides', - title: nls.localize('defaultConfigurations.title', "Default Configuration Overrides"), - properties: {} - }; - for (const defaultConfiguration of defaultConfigurations) { - for (const key in defaultConfiguration.defaults) { - const defaultValue = defaultConfiguration.defaults[key]; - if (OVERRIDE_PROPERTY_PATTERN.test(key) && typeof defaultValue === 'object') { - configurationNode.properties![key] = { - type: 'object', - default: defaultValue, - description: nls.localize('overrideSettings.description', "Configure editor settings to be overridden for {0} language.", key), - $ref: editorConfigurationSchemaId - }; - } - } - } - return Object.keys(configurationNode.properties!).length ? configurationNode : null; - } - private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, scope: ConfigurationScope = ConfigurationScope.WINDOW, overridable: boolean = false): string[] { scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope; overridable = configuration.overridable || overridable; @@ -273,7 +349,6 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } register(configuration); - this._onDidSchemaChange.fire(); } private updateSchemaForOverrideSettingsConfiguration(configuration: IConfigurationNode): void { diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index b5b8d401893..398b3dafec2 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -37,7 +37,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe // Listeners this._register(this.userConfiguration.onDidChangeConfiguration(userConfigurationModel => this.onDidChangeUserConfiguration(userConfigurationModel))); - this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDidRegisterConfiguration(configurationProperties))); + this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties))); } get configuration(): Configuration { @@ -99,7 +99,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe } } - private onDidRegisterConfiguration(keys: string[]): void { + private onDidDefaultConfigurationChange(keys: string[]): void { this._configuration.updateDefaultConfiguration(new DefaultConfigurationModel()); this.trigger(keys, ConfigurationTarget.DEFAULT); } diff --git a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts index c3acf23eeed..151bc66d10a 100644 --- a/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/services/configuration/common/configurationExtensionPoint.ts @@ -12,6 +12,7 @@ import { IConfigurationNode, IConfigurationRegistry, Extensions, editorConfigura import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject } from 'vs/base/common/types'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; const configurationRegistry = Registry.as(Extensions.Configuration); @@ -77,8 +78,6 @@ const configurationEntrySchema: IJSONSchema = { } }; -let registeredDefaultConfigurations: IDefaultConfigurationExtension[] = []; - // BEGIN VSCode extension point `configurationDefaults` const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'configurationDefaults', @@ -93,17 +92,32 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint { - registeredDefaultConfigurations = extensions.map(extension => { - const id = extension.description.identifier; - const name = extension.description.name; - const defaults = objects.deepClone(extension.value); - return { - id, name, defaults - }; - }); +defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => { + if (removed.length) { + const removedDefaultConfigurations: IDefaultConfigurationExtension[] = removed.map(extension => { + const id = extension.description.identifier; + const name = extension.description.name; + const defaults = objects.deepClone(extension.value); + return { + id, name, defaults + }; + }); + configurationRegistry.deregisterDefaultConfigurations(removedDefaultConfigurations); + } + if (added.length) { + const addedDefaultConfigurations = added.map(extension => { + const id = extension.description.identifier; + const name = extension.description.name; + const defaults = objects.deepClone(extension.value); + return { + id, name, defaults + }; + }); + configurationRegistry.registerDefaultConfigurations(addedDefaultConfigurations); + } }); // END VSCode extension point `configurationDefaults` @@ -121,12 +135,26 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint { - const configurations: IConfigurationNode[] = []; - function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser) { +const extensionConfigurations: Map = new Map(); + +configurationExtPoint.setHandler((extensions, { added, removed }) => { + + if (removed.length) { + const removedConfigurations: IConfigurationNode[] = []; + for (const extension of removed) { + const key = ExtensionIdentifier.toKey(extension.description.identifier); + removedConfigurations.push(...(extensionConfigurations.get(key) || [])); + extensionConfigurations.delete(key); + } + configurationRegistry.deregisterConfigurations(removedConfigurations); + } + + function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser): IConfigurationNode[] { + const configurations: IConfigurationNode[] = []; let configuration = objects.deepClone(node); if (configuration.title && (typeof configuration.title !== 'string')) { @@ -139,17 +167,26 @@ configurationExtPoint.setHandler(extensions => { configuration.contributedByExtension = true; configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value; configurations.push(configuration); + return configurations; } - for (let extension of extensions) { - const value = extension.value; - if (!Array.isArray(value)) { - handleConfiguration(value, extension); - } else { - value.forEach(v => handleConfiguration(v, extension)); + if (added.length) { + const addedConfigurations: IConfigurationNode[] = []; + for (let extension of added) { + const configurations: IConfigurationNode[] = []; + const value = extension.value; + if (!Array.isArray(value)) { + configurations.push(...handleConfiguration(value, extension)); + } else { + value.forEach(v => configurations.push(...handleConfiguration(v, extension))); + } + extensionConfigurations.set(ExtensionIdentifier.toKey(extension.description.identifier), configurations); + addedConfigurations.push(...configurations); } + + configurationRegistry.registerConfigurations(addedConfigurations, false); } - configurationRegistry.registerConfigurations(configurations, registeredDefaultConfigurations, false); + }); // END VSCode extension point `configuration` diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index ed1331a4e6b..41fbcfd2bc0 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -78,7 +78,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); this._register(Registry.as(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas())); - this._register(Registry.as(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); + this._register(Registry.as(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDefaultConfigurationChanged(configurationProperties))); this.workspaceEditingQueue = new Queue(); } From 24e8857cc5bfae52d898a24d852be8c468f1abb9 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Jan 2019 17:15:43 +0100 Subject: [PATCH 0434/1066] theme watching: support icon themes (for #66115) --- .../services/themes/electron-browser/fileIconThemeData.ts | 8 +++++++- .../themes/electron-browser/workbenchThemeService.ts | 6 ++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index b1fdad385d5..1269a1bfaed 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -23,6 +23,7 @@ export class FileIconThemeData implements IFileIconTheme { isLoaded: boolean; location?: URI; extensionData?: ExtensionData; + watch: boolean; styleSheetContent?: string; @@ -59,6 +60,7 @@ export class FileIconThemeData implements IFileIconTheme { themeData.description = iconTheme.description; themeData.location = iconThemeLocation; themeData.extensionData = extensionData; + themeData.watch = iconTheme._watch; themeData.isLoaded = false; return themeData; } @@ -77,6 +79,7 @@ export class FileIconThemeData implements IFileIconTheme { themeData.hidesExplorerArrows = false; themeData.isLoaded = true; themeData.extensionData = undefined; + themeData.watch = false; } return themeData; } @@ -91,6 +94,7 @@ export class FileIconThemeData implements IFileIconTheme { themeData.hasFolderIcons = false; themeData.hidesExplorerArrows = false; themeData.extensionData = undefined; + themeData.watch = false; return themeData; } @@ -109,6 +113,7 @@ export class FileIconThemeData implements IFileIconTheme { case 'hasFileIcons': case 'hidesExplorerArrows': case 'hasFolderIcons': + case 'watch': theme[key] = data[key]; break; case 'location': @@ -132,7 +137,8 @@ export class FileIconThemeData implements IFileIconTheme { styleSheetContent: this.styleSheetContent, hasFileIcons: this.hasFileIcons, hasFolderIcons: this.hasFolderIcons, - hidesExplorerArrows: this.hidesExplorerArrows + hidesExplorerArrows: this.hidesExplorerArrows, + watch: this.watch }); } } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index a4c951165a8..73235fecc2f 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -355,6 +355,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { if (this.watchedColorThemeLocation) { this.fileService.unwatchFileChanges(this.watchedColorThemeLocation); + this.watchedColorThemeLocation = null; } if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { this.watchedColorThemeLocation = newTheme.location; @@ -459,9 +460,10 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { if (this.watchedIconThemeLocation) { this.fileService.unwatchFileChanges(this.watchedIconThemeLocation); + this.watchedIconThemeLocation = null; } - this.watchedIconThemeLocation = iconThemeData.location; - if (this.watchedIconThemeLocation) { + if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { + this.watchedIconThemeLocation = iconThemeData.location; this.fileService.watchFileChanges(this.watchedIconThemeLocation); } } From a12582cb8665edc27ae3d9562021b788cda68af7 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 08:25:26 -0800 Subject: [PATCH 0435/1066] fix #66728 --- .../services/actions/electron-browser/menusExtensionPoint.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts index d9cddda6cb0..ce30b27bfbe 100644 --- a/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/electron-browser/menusExtensionPoint.ts @@ -230,6 +230,7 @@ namespace schema { const commandType: IJSONSchema = { type: 'object', + required: ['command', 'title'], properties: { command: { description: localize('vscode.extension.contributes.commandType.command', 'Identifier of the command to execute'), From 409293d564a65ae588fd528b07039e6f2d0ffa69 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 08:31:08 -0800 Subject: [PATCH 0436/1066] fix #66824 --- .../electron-browser/remoteFileService.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index da6dfd249e4..80078b6cade 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -563,17 +563,19 @@ export class RemoteFileService extends FileService { : Promise.resolve(null); return prepare.then(() => this._withProvider(source)).then(RemoteFileService._throwIfFileSystemIsReadonly).then(provider => { - return provider.rename(source, target, { overwrite }).then(() => { - return this.resolveFile(target); - }).then(fileStat => { - this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); - return fileStat; - }, err => { - const result = this._tryParseFileOperationResult(err); - if (result === FileOperationResult.FILE_MOVE_CONFLICT) { - throw new FileOperationError(localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), result); - } - throw err; + return RemoteFileService._mkdirp(provider, resources.dirname(target)).then(() => { + return provider.rename(source, target, { overwrite }).then(() => { + return this.resolveFile(target); + }).then(fileStat => { + this._onAfterOperation.fire(new FileOperationEvent(source, FileOperation.MOVE, fileStat)); + return fileStat; + }, err => { + const result = this._tryParseFileOperationResult(err); + if (result === FileOperationResult.FILE_MOVE_CONFLICT) { + throw new FileOperationError(localize('fileMoveConflict', "Unable to move/copy. File already exists at destination."), result); + } + throw err; + }); }); }); } From 3b4cea2aa0e0c1335540e31291bdfd9abb984fa1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 08:55:28 -0800 Subject: [PATCH 0437/1066] add telemetry to know how often multiple formatter situation occur, #41882 --- src/vs/editor/contrib/format/formatActions.ts | 70 +++++++++++++++---- 1 file changed, 55 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index fef43d6243c..3d9b801958c 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -27,6 +27,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CancellationToken } from 'vs/base/common/cancellation'; import { sequence } from 'vs/base/common/async'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -60,13 +61,31 @@ export const enum FormatRangeType { Selection, } -export function formatDocumentRange(workerService: IEditorWorkerService, editor: IActiveCodeEditor, rangeOrRangeType: Range | FormatRangeType, options: FormattingOptions, token: CancellationToken): Promise { +export function formatDocumentRange(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, rangeOrRangeType: Range | FormatRangeType, options: FormattingOptions, token: CancellationToken): Promise { - const provider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()).slice(0, 1); + const provider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()); if (provider.length === 0) { return Promise.reject(new NoProviderError()); } + // Know how often multiple providers clash and (for now) + // continue picking the 'first' provider + if (provider.length !== 1) { + /* __GDPR__ + "manyformatters" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('manyformatters', { + type: 'range', + language: editor.getModel().getLanguageIdentifier().language, + count: provider.length, + }); + provider.length = 1; + } + let allEdits: ISingleEditOperation[] = []; editor.pushUndoStop(); @@ -112,10 +131,28 @@ export function formatDocumentRange(workerService: IEditorWorkerService, editor: }); } -export function formatDocument(workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise { - const provider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel()).slice(0); +export function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise { + const provider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel()); if (provider.length === 0) { - return formatDocumentRange(workerService, editor, FormatRangeType.Full, options, token); + return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Full, options, token); + } + + // Know how often multiple providers clash and (for now) + // continue picking the 'first' provider + if (provider.length !== 1) { + /* __GDPR__ + "manyformatters" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('manyformatters', { + type: 'document', + language: editor.getModel().getLanguageIdentifier().language, + count: provider.length, + }); + provider.length = 1; } let allEdits: ISingleEditOperation[] = []; @@ -281,14 +318,14 @@ class FormatOnPaste implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.formatOnPaste'; - private editor: ICodeEditor; - private workerService: IEditorWorkerService; private callOnDispose: IDisposable[]; private callOnModel: IDisposable[]; - constructor(editor: ICodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) { - this.editor = editor; - this.workerService = workerService; + constructor( + private readonly editor: ICodeEditor, + @IEditorWorkerService private readonly workerService: IEditorWorkerService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { this.callOnDispose = []; this.callOnModel = []; @@ -336,7 +373,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution { const model = this.editor.getModel(); const { tabSize, insertSpaces } = model.getOptions(); - formatDocumentRange(this.workerService, this.editor, range, { tabSize, insertSpaces }, CancellationToken.None); + formatDocumentRange(this.telemetryService, this.workerService, this.editor, range, { tabSize, insertSpaces }, CancellationToken.None); } public getId(): string { @@ -378,8 +415,9 @@ export class FormatDocumentAction extends EditorAction { } const notificationService = accessor.get(INotificationService); const workerService = accessor.get(IEditorWorkerService); + const telemetryService = accessor.get(ITelemetryService); const { tabSize, insertSpaces } = editor.getModel().getOptions(); - return formatDocument(workerService, editor, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { + return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { if (NoProviderError.is(err)) { notificationService.info(nls.localize('no.documentprovider', "There is no document formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language)); } @@ -414,8 +452,9 @@ export class FormatSelectionAction extends EditorAction { } const notificationService = accessor.get(INotificationService); const workerService = accessor.get(IEditorWorkerService); + const telemetryService = accessor.get(ITelemetryService); const { tabSize, insertSpaces } = editor.getModel().getOptions(); - return formatDocumentRange(workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { + return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None).catch(err => { if (NoProviderError.is(err)) { notificationService.info(nls.localize('no.selectionprovider', "There is no selection formatter for '{0}'-files installed.", editor.getModel().getLanguageIdentifier().language)); } @@ -437,10 +476,11 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => { } const { tabSize, insertSpaces } = editor.getModel().getOptions(); const workerService = accessor.get(IEditorWorkerService); + const telemetryService = accessor.get(ITelemetryService); if (editor.getSelection().isEmpty()) { - return formatDocument(workerService, editor, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocument(telemetryService, workerService, editor, { tabSize, insertSpaces }, CancellationToken.None); } else { - return formatDocumentRange(workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); + return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); } }); From dc0e64414e37f8d89ae7ef3b40ce0cb0a949911d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 09:34:44 -0800 Subject: [PATCH 0438/1066] fix #65412 --- src/vs/editor/contrib/snippet/snippetParser.ts | 2 +- src/vs/editor/contrib/snippet/test/snippetParser.test.ts | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/snippet/snippetParser.ts b/src/vs/editor/contrib/snippet/snippetParser.ts index d869799518a..46d069cddbc 100644 --- a/src/vs/editor/contrib/snippet/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/snippetParser.ts @@ -930,7 +930,7 @@ export class SnippetParser { let escaped: string; if (escaped = this._accept(TokenType.Backslash, true)) { - escaped = this._accept(TokenType.Forwardslash, true) || escaped; + escaped = this._accept(TokenType.Backslash, true) || this._accept(TokenType.Forwardslash, true) || escaped; transform.appendChild(new Text(escaped)); continue; } diff --git a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts index e3dfdfd688b..60bcebba8b0 100644 --- a/src/vs/editor/contrib/snippet/test/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/snippetParser.test.ts @@ -748,4 +748,10 @@ suite('SnippetParser', () => { let [, , clone] = snippet.children; assertParent(clone); }); + + test('Backspace can\'t be escaped in snippet variable transforms #65412', function () { + + let snippet = new SnippetParser().parse('namespace ${TM_DIRECTORY/[\\/]/\\\\/g};'); + assertMarker(snippet, Text, Variable, Text); + }); }); From 172b5a5c5938c20a1a9e3d2cfc0b0167a3f83c31 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 10:20:57 -0800 Subject: [PATCH 0439/1066] delegate to opener service, #66741 --- .../api/electron-browser/mainThreadWindow.ts | 16 ++++++++-------- src/vs/workbench/api/node/extHostWindow.ts | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts index 752cba581d5..17680b7bbfe 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWindowService } from 'vs/platform/windows/common/windows'; -import { MainThreadWindowShape, ExtHostWindowShape, ExtHostContext, MainContext, IExtHostContext } from '../node/extHost.protocol'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { Event } from 'vs/base/common/event'; -import { UriComponents, URI } from 'vs/base/common/uri'; -import { ICommandService } from 'vs/platform/commands/common/commands'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape } from '../node/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadWindow) export class MainThreadWindow implements MainThreadWindowShape { @@ -20,7 +20,7 @@ export class MainThreadWindow implements MainThreadWindowShape { constructor( extHostContext: IExtHostContext, @IWindowService private readonly windowService: IWindowService, - @ICommandService private readonly commandService: ICommandService, + @IOpenerService private readonly openerService: IOpenerService, ) { this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow); @@ -39,6 +39,6 @@ export class MainThreadWindow implements MainThreadWindowShape { $openUri(uri: UriComponents): Promise { // todo@joh turn this around and let the command work with // the proper implementation - return this.commandService.executeCommand('_workbench.open', URI.revive(uri)); + return this.openerService.open(URI.revive(uri)).then(() => undefined); } } diff --git a/src/vs/workbench/api/node/extHostWindow.ts b/src/vs/workbench/api/node/extHostWindow.ts index e995982451a..244dd5675f5 100644 --- a/src/vs/workbench/api/node/extHostWindow.ts +++ b/src/vs/workbench/api/node/extHostWindow.ts @@ -7,6 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext } from './extHost.protocol'; import { WindowState } from 'vscode'; import { URI } from 'vs/base/common/uri'; +import { Schemas } from 'vs/base/common/network'; export class ExtHostWindow implements ExtHostWindowShape { @@ -37,6 +38,9 @@ export class ExtHostWindow implements ExtHostWindowShape { } openUri(uri: URI): Promise { + if (uri.scheme === Schemas.command) { + return Promise.reject(`Invalid scheme '${uri.scheme}'`); + } return this._proxy.$openUri(uri); } } From b9b1f0306c718de2d52aa2e9ba38b3ba0ba3b402 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 21 Jan 2019 18:22:52 +0000 Subject: [PATCH 0440/1066] Fix #66573 --- src/vs/workbench/parts/search/browser/searchResultsView.ts | 4 ++-- src/vs/workbench/parts/search/common/searchModel.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 01ced65b820..20bec999903 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -25,7 +25,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace 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 } from 'vs/workbench/parts/search/common/searchModel'; +import { FileMatch, FolderMatch, Match, RenderableMatch, SearchModel, BaseFolderMatch } from 'vs/workbench/parts/search/common/searchModel'; interface IFolderMatchTemplate { label: IResourceLabel; @@ -57,7 +57,7 @@ export class SearchDelegate implements IListVirtualDelegate { } getTemplateId(element: RenderableMatch): string { - if (element instanceof FolderMatch) { + if (element instanceof BaseFolderMatch) { return FolderMatchRenderer.TEMPLATE_ID; } else if (element instanceof FileMatch) { return FileMatchRenderer.TEMPLATE_ID; diff --git a/src/vs/workbench/parts/search/common/searchModel.ts b/src/vs/workbench/parts/search/common/searchModel.ts index c6582f58457..f254431f6de 100644 --- a/src/vs/workbench/parts/search/common/searchModel.ts +++ b/src/vs/workbench/parts/search/common/searchModel.ts @@ -1006,7 +1006,7 @@ export class SearchModel extends Disposable { export type FileMatchOrMatch = FileMatch | Match; -export type RenderableMatch = FolderMatch | FileMatch | Match; +export type RenderableMatch = BaseFolderMatch | FolderMatch | FileMatch | Match; export class SearchWorkbenchService implements ISearchWorkbenchService { From bcc2281e45d3519e4fe401ecb0af99976b247458 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Jan 2019 17:52:46 -0800 Subject: [PATCH 0441/1066] Rename CodeAction.canAutoApply -> CodeAction.preferred Part of #62110 Use the more generic name as suggested in https://github.com/Dart-Code/Dart-Code/issues/1393. This makes the intent of the field more clear and also allows us to extend the concept of preferred code actions to refactorings and other classes of code actions Experimentally also allows a `preferred` value for `apply` when configuring code aciton keyboard shortcuts. This applies the preferred code action returned from the list of code actions returned --- .../src/features/quickFix.ts | 2 +- src/vs/editor/common/modes.ts | 2 +- .../editor/contrib/codeAction/codeAction.ts | 2 +- .../contrib/codeAction/codeActionCommands.ts | 23 ++++++++++++++----- .../contrib/codeAction/codeActionTrigger.ts | 9 ++++---- src/vs/monaco.d.ts | 2 +- src/vs/vscode.proposed.d.ts | 9 ++++---- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- .../api/node/extHostLanguageFeatures.ts | 2 +- 9 files changed, 33 insertions(+), 20 deletions(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 0b4fa587ec0..16b2069c232 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -270,7 +270,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { title: '' }; if (tsAction.fixName === 'spelling') { - codeAction.canAutoApply = true; + codeAction.isPreferred = true; } return codeAction; } diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 80eb6e5d057..3e6e7bcc4cd 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -516,7 +516,7 @@ export interface CodeAction { edit?: WorkspaceEdit; diagnostics?: IMarkerData[]; kind?: string; - canAutoApply?: boolean; + isPreferred?: boolean; } /** diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 8d012d07be3..d203fb26d8c 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -72,7 +72,7 @@ export function getCodeActions( function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean { return action && isValidActionKind(filter, action.kind) - && (filter && filter.autoFixesOnly ? !!action.canAutoApply : true); + && (filter && filter.onlyIncludePreferredActions ? !!action.isPreferred : true); } function isValidActionKind(filter: CodeActionFilter | undefined, kind: string | undefined): boolean { diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 072d6b4ea58..673688ef41f 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -90,13 +90,23 @@ export class QuickFixController implements IEditorContribution { if (newState.trigger.filter && newState.trigger.filter.kind) { // Triggered for specific scope - // Apply if we only have one action or requested autoApply, otherwise show menu newState.actions.then(fixes => { - if (fixes.length > 0 && newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) { - this._onApplyCodeAction(fixes[0]); - } else { - this._codeActionContextMenu.show(newState.actions, newState.position); + if (fixes.length > 0) { + // Apply if we only have one action or requested autoApply + if (newState.trigger.autoApply === CodeActionAutoApply.First || (newState.trigger.autoApply === CodeActionAutoApply.IfSingle && fixes.length === 1)) { + this._onApplyCodeAction(fixes[0]); + return; + } + + // Or if we have a single preferred action + const preferred = fixes.filter(fix => fix.isPreferred); + if (preferred.length === 0) { + this._onApplyCodeAction(preferred[0]); + return; + } } + this._codeActionContextMenu.show(newState.actions, newState.position); + }).catch(onUnexpectedError); } else if (newState.trigger.type === 'manual') { this._codeActionContextMenu.show(newState.actions, newState.position); @@ -221,6 +231,7 @@ class CodeActionCommandArgs { case 'first': return CodeActionAutoApply.First; case 'never': return CodeActionAutoApply.Never; case 'ifsingle': return CodeActionAutoApply.IfSingle; + case 'preferred': return CodeActionAutoApply.Preferred; default: return defaultAutoApply; } } @@ -382,7 +393,7 @@ export class AutoFixAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), - { kind: CodeActionKind.QuickFix, autoFixesOnly: true }, + { kind: CodeActionKind.QuickFix, onlyIncludePreferredActions: true }, CodeActionAutoApply.IfSingle); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index ad1bbfe85b1..305470be4a4 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -24,15 +24,16 @@ export class CodeActionKind { } export const enum CodeActionAutoApply { - IfSingle = 1, - First = 2, - Never = 3 + IfSingle, + First, + Preferred, + Never, } export interface CodeActionFilter { readonly kind?: CodeActionKind; readonly includeSourceActions?: boolean; - readonly autoFixesOnly?: boolean; + readonly onlyIncludePreferredActions?: boolean; } export interface CodeActionTrigger { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0cd4c9504fd..a15aadccd86 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4848,7 +4848,7 @@ declare namespace monaco.languages { edit?: WorkspaceEdit; diagnostics?: editor.IMarkerData[]; kind?: string; - canAutoApply?: boolean; + isPreferred?: boolean; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f95e97c3ced..8cbeff02145 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1110,14 +1110,15 @@ declare module 'vscode' { } //#endregion - //#region CodeAction.canAutoApply - mjbvz + //#region CodeAction.isPreferred - mjbvz export interface CodeAction { /** - * If the action can be safely automatically applied without the user selecting it from a list. + * If the action is a prefered action or fix to take. * - * Set this on quick fixes to indicate that the fix properly addresses the underlying error. + * 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. */ - canAutoApply?: boolean; + isPreferred?: boolean; } //#endregion diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index d8e363a64a2..b39e87c00c5 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -864,7 +864,7 @@ export interface CodeActionDto { diagnostics?: IMarkerData[]; command?: modes.Command; kind?: string; - canAutoApply?: boolean; + isPreferred?: boolean; } export interface ExtHostLanguageFeaturesShape { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index e12de7857c0..b8b1d17ff4c 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -347,7 +347,7 @@ class CodeActionAdapter { diagnostics: candidate.diagnostics && candidate.diagnostics.map(typeConvert.Diagnostic.from), edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit), kind: candidate.kind && candidate.kind.value, - canAutoApply: candidate.canAutoApply, + isPreferred: candidate.isPreferred, }); } } From e42d49be2b4b940cdbb172650e0a6e1a69d47a29 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Jan 2019 17:59:02 -0800 Subject: [PATCH 0442/1066] Extraction and convert to async --- .../contrib/codeAction/codeActionWidget.ts | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index f30b9735d54..1338dc3d729 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -27,38 +27,32 @@ export class CodeActionContextMenu { private readonly _onApplyCodeAction: (action: CodeAction) => Promise ) { } - show(fixes: Promise, at?: { x: number; y: number } | Position) { - const actionsPromise = fixes.then(value => { - return value.map(action => { - return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { - return always( - this._onApplyCodeAction(action), - () => this._onDidExecuteCodeAction.fire(undefined)); - }); - }); - }).then(actions => { - if (!this._editor.getDomNode()) { - // cancel when editor went off-dom - return Promise.reject(canceled()); - } - return actions; + async show(actionsToShow: Promise, at?: { x: number; y: number } | Position): Promise { + const codeActions = await actionsToShow; + if (!this._editor.getDomNode()) { + // cancel when editor went off-dom + return Promise.reject(canceled()); + } + const actions = codeActions.map(action => this.codeActionToAction(action)); + this._contextMenuService.showContextMenu({ + getAnchor: () => { + if (Position.isIPosition(at)) { + at = this._toCoords(at); + } + return at || { x: 0, y: 0 }; + }, + getActions: () => actions, + onHide: () => { + this._visible = false; + this._editor.focus(); + }, + autoSelectFirstItem: true }); + } - actionsPromise.then(actions => { - this._contextMenuService.showContextMenu({ - getAnchor: () => { - if (Position.isIPosition(at)) { - at = this._toCoords(at); - } - return at || { x: 0, y: 0 }; - }, - getActions: () => actions, - onHide: () => { - this._visible = false; - this._editor.focus(); - }, - autoSelectFirstItem: true - }); + private codeActionToAction(action: CodeAction): Action { + return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { + return always(this._onApplyCodeAction(action), () => this._onDidExecuteCodeAction.fire(undefined)); }); } From e2fed923623d3c53c5acfb5bc0636d1441fbd740 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Jan 2019 18:21:16 -0800 Subject: [PATCH 0443/1066] Mark preferred code actions with a star in the code action list #66702 Experimentally mark preferred code actions with a star while we come up with better UX for this --- src/vs/editor/contrib/codeAction/codeActionWidget.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionWidget.ts b/src/vs/editor/contrib/codeAction/codeActionWidget.ts index 1338dc3d729..7b4b5fb5acb 100644 --- a/src/vs/editor/contrib/codeAction/codeActionWidget.ts +++ b/src/vs/editor/contrib/codeAction/codeActionWidget.ts @@ -51,8 +51,12 @@ export class CodeActionContextMenu { } private codeActionToAction(action: CodeAction): Action { - return new Action(action.command ? action.command.id : action.title, action.title, undefined, true, () => { - return always(this._onApplyCodeAction(action), () => this._onDidExecuteCodeAction.fire(undefined)); + 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)); }); } From 55be1d8d280b20d20befae39df6437ddcfd9f735 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Jan 2019 18:27:36 -0800 Subject: [PATCH 0444/1066] Trying to use less state in lightbulb api --- .../contrib/codeAction/codeActionCommands.ts | 8 +++----- .../contrib/codeAction/lightBulbWidget.ts | 19 ++++++++++--------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 673688ef41f..a6fc6fa3662 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -117,7 +117,7 @@ export class QuickFixController implements IEditorContribution { if (this._codeActionContextMenu.isVisible) { this._codeActionContextMenu.show(newState.actions, newState.position); } else { - this._lightBulbWidget.state = newState; + this._lightBulbWidget.tryShow(newState); } } } else { @@ -129,10 +129,8 @@ export class QuickFixController implements IEditorContribution { return QuickFixController.ID; } - private _handleLightBulbSelect(coords: { x: number, y: number }): void { - if (this._lightBulbWidget.state.type === CodeActionsState.Type.Triggered) { - this._codeActionContextMenu.show(this._lightBulbWidget.state.actions, coords); - } + private _handleLightBulbSelect(e: { x: number, y: number, state: CodeActionsState.Triggered }): void { + this._codeActionContextMenu.show(e.state.actions, e); } public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Promise { diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 3dcbf4d4efa..1138f95b4f0 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -20,7 +20,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { private readonly _domNode: HTMLDivElement; private readonly _editor: ICodeEditor; private readonly _disposables: IDisposable[] = []; - private readonly _onClick = new Emitter<{ x: number, y: number }>(); + private readonly _onClick = new Emitter<{ x: number; y: number; state: CodeActionsState.Triggered }>(); readonly onClick: Event<{ x: number, y: number }> = this._onClick.event; @@ -40,11 +40,15 @@ export class LightBulbWidget implements IDisposable, IContentWidget { this._disposables.push(this._editor.onDidChangeModelContent(_ => { // cancel when the line in question has been removed const editorModel = this._editor.getModel(); - if (this.state.type !== CodeActionsState.Type.Triggered || !editorModel || this.state.position.lineNumber >= editorModel.getLineCount()) { + if (this._state.type !== CodeActionsState.Type.Triggered || !editorModel || this._state.position.lineNumber >= editorModel.getLineCount()) { this._futureFixes.cancel(); } })); this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => { + if (this._state.type !== CodeActionsState.Type.Triggered) { + return; + } + // Make sure that focus / cursor location is not lost when clicking widget icon this._editor.focus(); // a bit of extra work to make sure the menu @@ -53,13 +57,14 @@ export class LightBulbWidget implements IDisposable, IContentWidget { const { lineHeight } = this._editor.getConfiguration(); let pad = Math.floor(lineHeight / 3); - if (this._position && this._state.type === CodeActionsState.Type.Triggered && this._position.position !== null && this._position.position.lineNumber < this._state.position.lineNumber) { + if (this._position && this._position.position !== null && this._position.position.lineNumber < this._state.position.lineNumber) { pad += lineHeight; } this._onClick.fire({ x: e.posx, - y: top + height + pad + y: top + height + pad, + state: this._state }); })); this._disposables.push(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { @@ -100,7 +105,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { return this._position; } - set state(newState: CodeActionsState.State) { + tryShow(newState: CodeActionsState.State) { if (newState.type !== CodeActionsState.Type.Triggered || this._position && (!newState.position || this._position.position && this._position.position.lineNumber !== newState.position.lineNumber)) { // hide when getting a 'hide'-request or when currently @@ -131,10 +136,6 @@ export class LightBulbWidget implements IDisposable, IContentWidget { }); } - get state(): CodeActionsState.State { - return this._state; - } - set title(value: string) { this._domNode.title = value; } From e84eca9c8c81ff16c8762c197baafb29c5baa899 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 17 Jan 2019 18:30:56 -0800 Subject: [PATCH 0445/1066] Extend Disposable --- .../contrib/codeAction/lightBulbWidget.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index 1138f95b4f0..65dc62e79bc 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -6,45 +6,45 @@ import * as dom from 'vs/base/browser/dom'; import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { Emitter, Event } from 'vs/base/common/event'; -import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./lightBulbWidget'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { TextModel } from 'vs/editor/common/model/textModel'; import { CodeActionsState } from './codeActionModel'; -export class LightBulbWidget implements IDisposable, IContentWidget { +export class LightBulbWidget extends Disposable implements IContentWidget { private static readonly _posPref = [ContentWidgetPositionPreference.EXACT]; private readonly _domNode: HTMLDivElement; private readonly _editor: ICodeEditor; - private readonly _disposables: IDisposable[] = []; - private readonly _onClick = new Emitter<{ x: number; y: number; state: CodeActionsState.Triggered }>(); - readonly onClick: Event<{ x: number, y: number }> = this._onClick.event; + private readonly _onClick = this._register(new Emitter<{ x: number; y: number; state: CodeActionsState.Triggered }>()); + public readonly onClick = this._onClick.event; private _position: IContentWidgetPosition | null; private _state: CodeActionsState.State = CodeActionsState.Empty; private _futureFixes = new CancellationTokenSource(); constructor(editor: ICodeEditor) { + super(); this._domNode = document.createElement('div'); this._domNode.className = 'lightbulb-glyph'; this._editor = editor; this._editor.addContentWidget(this); - this._disposables.push(this._editor.onDidChangeModel(_ => this._futureFixes.cancel())); - this._disposables.push(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel())); - this._disposables.push(this._editor.onDidChangeModelContent(_ => { + this._register(this._editor.onDidChangeModel(_ => this._futureFixes.cancel())); + this._register(this._editor.onDidChangeModelLanguage(_ => this._futureFixes.cancel())); + this._register(this._editor.onDidChangeModelContent(_ => { // cancel when the line in question has been removed const editorModel = this._editor.getModel(); if (this._state.type !== CodeActionsState.Type.Triggered || !editorModel || this._state.position.lineNumber >= editorModel.getLineCount()) { this._futureFixes.cancel(); } })); - this._disposables.push(dom.addStandardDisposableListener(this._domNode, 'click', e => { + this._register(dom.addStandardDisposableListener(this._domNode, 'click', e => { if (this._state.type !== CodeActionsState.Type.Triggered) { return; } @@ -67,7 +67,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { state: this._state }); })); - this._disposables.push(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { + this._register(dom.addDisposableListener(this._domNode, 'mouseenter', (e: MouseEvent) => { if ((e.buttons & 1) !== 1) { return; } @@ -80,7 +80,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { monitor.dispose(); }); })); - this._disposables.push(this._editor.onDidChangeConfiguration(e => { + this._register(this._editor.onDidChangeConfiguration(e => { // hide when told to do so if (e.contribInfo && !this._editor.getConfiguration().contribInfo.lightbulbEnabled) { this.hide(); @@ -89,7 +89,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget { } dispose(): void { - dispose(this._disposables); + super.dispose(); this._editor.removeContentWidget(this); } From 59ce7f3b28869ab14668f9a6ae66e48367af53ce Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 13:46:37 -0800 Subject: [PATCH 0446/1066] Fix markdown preview code highlighting Fixes #66772 --- .../src/markdownEngine.ts | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index a9fd3a1ab8a..ac2743a487d 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -64,28 +64,7 @@ export class MarkdownEngine { private async getEngine(config: MarkdownItConfig): Promise { if (!this.md) { this.md = import('markdown-it').then(async markdownIt => { - const hljs = await import('highlight.js'); - let md = markdownIt({ - html: true, - highlight: (str: string, lang?: string) => { - // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 - if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { - lang = 'jsx'; - } - if (lang && lang.toLocaleLowerCase() === 'json5') { - lang = 'json'; - } - if (lang && lang.toLocaleLowerCase() === 'c#') { - lang = 'cs'; - } - if (lang && hljs.getLanguage(lang)) { - try { - return `

`; - } catch (error) { } - } - return `
${md!.utils.escapeHtml(str)}
`; - } - }); + let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { try { @@ -151,7 +130,10 @@ export class MarkdownEngine { public async render(document: SkinnyTextDocument): Promise { const config = this.getConfig(document.uri); const engine = await this.getEngine(config); - return engine.renderer.render(this.tokenize(document, config, engine), engine, {}); + return engine.renderer.render(this.tokenize(document, config, engine), { + ...(await getMarkdownOptions(() => engine)), + ...config + }, {}); } public async parse(document: SkinnyTextDocument): Promise { @@ -294,4 +276,31 @@ export class MarkdownEngine { } }; } -} \ No newline at end of file +} + +async function getMarkdownOptions(md: () => MarkdownIt) { + const hljs = await import('highlight.js'); + return { + html: true, + highlight: (str: string, lang?: string) => { + console.log(123); + // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 + if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { + lang = 'jsx'; + } + if (lang && lang.toLocaleLowerCase() === 'json5') { + lang = 'json'; + } + if (lang && lang.toLocaleLowerCase() === 'c#') { + lang = 'cs'; + } + if (lang && hljs.getLanguage(lang)) { + try { + return `
${hljs.highlight(lang, str, true).value}
`; + } + catch (error) { } + } + return `
${md().utils.escapeHtml(str)}
`; + } + }; +} From b555eb888b6303916b22c5b9a43e4bee67bb17fc Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 21 Jan 2019 17:51:30 +0100 Subject: [PATCH 0447/1066] past file action not needed, use command instead --- .../files/electron-browser/fileActions.ts | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index bb96fe20597..857a89165cd 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -434,27 +434,6 @@ class BaseDeleteFileAction extends BaseErrorReportingAction { } let pasteShouldMove = false; -// Copy File/Folder -class CopyFileAction extends BaseErrorReportingAction { - - constructor( - private elements: ExplorerItem[], - @INotificationService notificationService: INotificationService, - @IClipboardService private readonly clipboardService: IClipboardService - ) { - super('filesExplorer.copy', COPY_FILE_LABEL, notificationService); - } - - public run(): Promise { - - // Write to clipboard as file/folder to copy - this.clipboardService.writeResources(this.elements.map(e => e.resource)); - pasteShouldMove = false; - - return Promise.resolve(null); - } -} - // Paste File/Folder class PasteFileAction extends BaseErrorReportingAction { @@ -1123,13 +1102,14 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => { }; export const copyFileHandler = (accessor: ServicesAccessor) => { - const instantationService = accessor.get(IInstantiationService); + const clipboardService = accessor.get(IClipboardService); const listService = accessor.get(IListService); const explorerContext = getContext(listService.lastFocusedList); + const explorerService = accessor.get(IExplorerService); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; - - const copyFileAction = instantationService.createInstance(CopyFileAction, stats); - return copyFileAction.run(); + explorerService.setToCopy(stats, false); + clipboardService.writeResources(stats.map(e => e.resource)); + pasteShouldMove = false; }; export const cutFileHandler = (accessor: ServicesAccessor) => { From ff82242210d1e31e1c910a57e769f6457dad7c7a Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 21 Jan 2019 17:52:06 +0100 Subject: [PATCH 0448/1066] explorer: copy path and copy relative path to their own groups --- .../files/electron-browser/fileActions.contribution.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 f2b6c333c94..13a91fe33f6 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -115,7 +115,7 @@ const PASTE_FILE_ID = 'filesExplorer.paste'; KeybindingsRegistry.registerCommandAndKeybindingRule({ id: PASTE_FILE_ID, weight: KeybindingWeight.WorkbenchContrib + explorerCommandsWeightBonus, - when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext), + when: ContextKeyExpr.and(FilesExplorerFocusCondition, ExplorerResourceNotReadonlyContext, FileCopiedContext), primary: KeyMod.CtrlCmd | KeyCode.KEY_V, handler: pasteFileHandler }); @@ -441,20 +441,20 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { command: { id: PASTE_FILE_ID, title: PASTE_FILE_LABEL, - precondition: FileCopiedContext + precondition: ContextKeyExpr.and(ExplorerResourceNotReadonlyContext, FileCopiedContext) }, when: ExplorerFolderContext }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: '5_cutcopypaste', + group: '6_copypath', order: 30, command: copyPathCommand, when: ResourceContextKey.IsFileSystemResource }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: '5_cutcopypaste', + group: '6_copypath', order: 30, command: copyRelativePathCommand, when: ResourceContextKey.IsFileSystemResource From b4971f371c4253321b3b4353ab627e04f9c09b6d Mon Sep 17 00:00:00 2001 From: isidor Date: Mon, 21 Jan 2019 23:10:31 +0100 Subject: [PATCH 0449/1066] explorer: polish cut action --- src/vs/workbench/parts/files/common/files.ts | 3 +++ .../files/electron-browser/explorerService.ts | 22 ++++++++++++++++++- .../files/electron-browser/fileActions.ts | 10 +++++---- .../media/explorerviewlet.css | 4 ++++ .../electron-browser/views/explorerView.ts | 16 +++++++++----- .../electron-browser/views/explorerViewer.ts | 3 +++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 6472cf2147e..b9d0f917690 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -45,11 +45,14 @@ export interface IExplorerService { readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; readonly onDidSelectItem: Event<{ item?: ExplorerItem, reveal?: boolean }>; + readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }>; setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; findClosest(resource: URI): ExplorerItem | null; refresh(): void; + setToCopy(stats: ExplorerItem[], cut: boolean): void; + isCut(stat: ExplorerItem): boolean; /** * Selects and reveal the file element provided by the given resource if its found in the explorer. Will try to diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 766e6dc5508..ae302bff94b 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -16,6 +16,7 @@ import { ResourceGlobMatcher } from 'vs/workbench/electron-browser/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IExpression } from 'vs/base/common/glob'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { const scope = root ? { resource: root } : undefined; @@ -33,15 +34,18 @@ export class ExplorerService implements IExplorerService { private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>(); + private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }>(); private disposables: IDisposable[] = []; private editableStats = new Map(); private _sortOrder: SortOrder; + private cutItems: ExplorerItem[]; constructor( @IFileService private fileService: IFileService, @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, - @IWorkspaceContextService private contextService: IWorkspaceContextService + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IClipboardService private clipboardService: IClipboardService ) { } get roots(): ExplorerItem[] { @@ -64,6 +68,10 @@ export class ExplorerService implements IExplorerService { return this._onDidSelectItem.event; } + get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }> { + return this._onDidCopyItems.event; + } + get sortOrder(): SortOrder { return this._sortOrder; } @@ -103,6 +111,18 @@ export class ExplorerService implements IExplorerService { this._onDidChangeEditable.fire(stat); } + setToCopy(items: ExplorerItem[], cut: boolean): void { + const previouslyCutItems = this.cutItems; + this.cutItems = cut ? items : undefined; + this.clipboardService.writeResources(items.map(s => s.resource)); + + this._onDidCopyItems.fire({ items, cut, previouslyCutItems }); + } + + isCut(item: ExplorerItem): boolean { + return this.cutItems && this.cutItems.indexOf(item) >= 0; + } + getEditableData(stat: ExplorerItem): IEditableData | undefined { return this.editableStats.get(stat); } diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 857a89165cd..05b535dbfc3 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -475,6 +475,10 @@ class PasteFileAction extends BaseErrorReportingAction { // Copy File const promise = pasteShouldMove ? this.fileService.moveFile(fileToPaste, targetFile) : this.fileService.copyFile(fileToPaste, targetFile); return promise.then(stat => { + if (pasteShouldMove) { + // Cut is done. Make sure to clear cut state. + this.explorerService.setToCopy([], false); + } if (!stat.isDirectory) { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } }); } @@ -1102,22 +1106,20 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => { }; export const copyFileHandler = (accessor: ServicesAccessor) => { - const clipboardService = accessor.get(IClipboardService); const listService = accessor.get(IListService); const explorerContext = getContext(listService.lastFocusedList); const explorerService = accessor.get(IExplorerService); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; explorerService.setToCopy(stats, false); - clipboardService.writeResources(stats.map(e => e.resource)); pasteShouldMove = false; }; export const cutFileHandler = (accessor: ServicesAccessor) => { const listService = accessor.get(IListService); const explorerContext = getContext(listService.lastFocusedList); - const clipboardService = accessor.get(IClipboardService); + const explorerService = accessor.get(IExplorerService); const stats = explorerContext.selection.length > 1 ? explorerContext.selection : [explorerContext.stat]; - clipboardService.writeResources(stats.map(s => s.resource)); + explorerService.setToCopy(stats, true); pasteShouldMove = true; }; 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 06bcd022031..a3a6b3451ec 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -47,6 +47,10 @@ flex: 1; } +.explorer-viewlet .explorer-item.cut { + opacity: 0.3; +} + .explorer-viewlet .explorer-item.explorer-item-edited .label-name { flex: 0; /* do not steal space when label is hidden because we are in edit mode */ } 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 e9ef9e3702a..ee4d8910b65 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -37,7 +37,6 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -83,7 +82,6 @@ export class ExplorerView extends ViewletPanel { @IThemeService private readonly themeService: IWorkbenchThemeService, @IListService private readonly listService: IListService, @IMenuService private readonly menuService: IMenuService, - @IClipboardService private readonly clipboardService: IClipboardService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IExplorerService private readonly explorerService: IExplorerService, @IStorageService private readonly storageService: IStorageService @@ -185,6 +183,7 @@ export class ExplorerView extends ViewletPanel { } })); this.disposables.push(this.explorerService.onDidSelectItem(e => this.onSelectItem(e.item, e.reveal))); + this.disposables.push(this.explorerService.onDidCopyItems(e => this.onCopyItems(e.items, e.cut, e.previouslyCutItems))); // Update configuration const configuration = this.configurationService.getValue(); @@ -368,9 +367,6 @@ export class ExplorerView extends ViewletPanel { private onContextMenu(e: ITreeContextMenuEvent): void { const stat = e.element; - // update dynamic contexts - this.fileCopiedContextKey.set(this.clipboardService.hasResources()); - const selection = this.tree.getSelection(); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, @@ -484,6 +480,16 @@ export class ExplorerView extends ViewletPanel { }); } + private onCopyItems(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[]): void { + this.fileCopiedContextKey.set(stats.length > 0); + if (previousCut) { + previousCut.forEach(item => this.tree.refresh(item)); + } + if (cut) { + stats.forEach(s => this.tree.refresh(s)); + } + } + collapseAll(): void { this.tree.collapseAll(); } 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 a40f55ef664..e8a55cb5455 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -140,6 +140,9 @@ export class FilesRenderer implements ITreeRenderer Date: Mon, 21 Jan 2019 14:38:34 -0800 Subject: [PATCH 0450/1066] Make sure we send proper arguments to completion item command Fixes #66870 --- src/vs/editor/contrib/suggest/suggestController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index e0d9982667f..5b033471cc7 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -268,7 +268,7 @@ export class SuggestController implements IEditorContribution { } else { // exec command, done - this._commandService.executeCommand(suggestion.command.id, suggestion.command.arguments ? [...suggestion.command.arguments] : []).catch(onUnexpectedError); + this._commandService.executeCommand(suggestion.command.id, ...(suggestion.command.arguments ? [...suggestion.command.arguments] : [])).catch(onUnexpectedError); this._model.cancel(); } From 32629a8125499c3831c5e37a3e2a0d305827871d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 14:41:14 -0800 Subject: [PATCH 0451/1066] update reference view extension, #64376 --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index b730189c0f1..5bc85092c84 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.21", + "version": "0.0.23", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From 3dd928224bbb671ced29265a1d4c9c8e7586734c Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 22 Jan 2019 00:02:19 +0100 Subject: [PATCH 0452/1066] fix null checks --- src/vs/workbench/parts/files/common/files.ts | 2 +- .../parts/files/electron-browser/explorerService.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index b9d0f917690..a9e24086881 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -45,7 +45,7 @@ export interface IExplorerService { readonly onDidChangeItem: Event; readonly onDidChangeEditable: Event; readonly onDidSelectItem: Event<{ item?: ExplorerItem, reveal?: boolean }>; - readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }>; + readonly onDidCopyItems: Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>; setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index ae302bff94b..a439f87af54 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -34,11 +34,11 @@ export class ExplorerService implements IExplorerService { private _onDidChangeItem = new Emitter(); private _onDidChangeEditable = new Emitter(); private _onDidSelectItem = new Emitter<{ item?: ExplorerItem, reveal?: boolean }>(); - private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }>(); + private _onDidCopyItems = new Emitter<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }>(); private disposables: IDisposable[] = []; private editableStats = new Map(); private _sortOrder: SortOrder; - private cutItems: ExplorerItem[]; + private cutItems: ExplorerItem[] | undefined; constructor( @IFileService private fileService: IFileService, @@ -68,7 +68,7 @@ export class ExplorerService implements IExplorerService { return this._onDidSelectItem.event; } - get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] }> { + get onDidCopyItems(): Event<{ items: ExplorerItem[], cut: boolean, previouslyCutItems: ExplorerItem[] | undefined }> { return this._onDidCopyItems.event; } @@ -120,7 +120,7 @@ export class ExplorerService implements IExplorerService { } isCut(item: ExplorerItem): boolean { - return this.cutItems && this.cutItems.indexOf(item) >= 0; + return !!this.cutItems && this.cutItems.indexOf(item) >= 0; } getEditableData(stat: ExplorerItem): IEditableData | undefined { From 96ab284b4e5f7a1d77c1be6e127be3c6accb3a0a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 15:02:15 -0800 Subject: [PATCH 0453/1066] move function to `env`, rename to `openItem` #66741 --- src/vs/vscode.proposed.d.ts | 4 ++-- src/vs/workbench/api/node/extHost.api.impl.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 8cbeff02145..467f24620cf 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -18,11 +18,11 @@ declare module 'vscode' { //#region Joh - vscode.open - export namespace window { + export namespace env { /** * */ - export function open(uri: Uri): void; + export function openItem(uri: Uri): void; } //#endregion diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 6cb7c089487..86fc24670b2 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -255,7 +255,10 @@ export function createApiFactory( }, get clipboard(): vscode.Clipboard { return extHostClipboard; - } + }, + openItem: proposedApiFunction(extension, (uri: URI) => { + return extHostWindow.openUri(uri); + }) }); // namespace: extensions @@ -496,10 +499,7 @@ export function createApiFactory( }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); - }, - open: proposedApiFunction(extension, (uri: URI) => { - return extHostWindow.openUri(uri); - }) + } }; // namespace: workspace From e83c7a08a9340f817f708aed0e773d755a7a52f6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 16:29:41 -0800 Subject: [PATCH 0454/1066] use fallback-command instead of text, make fallback configurable via keybindings file, #65587 --- src/vs/editor/browser/editorExtensions.ts | 6 +++--- src/vs/editor/contrib/suggest/suggestController.ts | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 20cdbad8f5c..071893e858b 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -116,7 +116,7 @@ export abstract class Command { //#region EditorCommand export interface IContributionCommandOptions extends ICommandOptions { - handler: (controller: T) => void; + handler: (controller: T, args: any) => void; } export interface EditorControllerCommand { new(opts: IContributionCommandOptions): EditorCommand; @@ -128,7 +128,7 @@ export abstract class EditorCommand extends Command { */ public static bindToContribution(controllerGetter: (editor: ICodeEditor) => T): EditorControllerCommand { return class EditorControllerCommandImpl extends EditorCommand { - private _callback: (controller: T) => void; + private _callback: (controller: T, args: any) => void; constructor(opts: IContributionCommandOptions) { super(opts); @@ -139,7 +139,7 @@ export abstract class EditorCommand extends Command { public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { let controller = controllerGetter(editor); if (controller) { - this._callback(controllerGetter(editor)); + this._callback(controllerGetter(editor), args); } } }; diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 5b033471cc7..05e535ac302 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -12,7 +12,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, EditorCommand, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; -import { IEditorContribution, ScrollType, Handler } from 'vs/editor/common/editorCommon'; +import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { CompletionItemProvider, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; @@ -32,6 +32,7 @@ import { Event } from 'vs/base/common/event'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { IdleValue } from 'vs/base/common/async'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; +import { isObject } from 'vs/base/common/types'; class AcceptOnCharacterOracle { @@ -303,7 +304,7 @@ export class SuggestController implements IEditorContribution { } } - triggerSuggestAndAcceptBest(defaultTypeText: string): void { + triggerSuggestAndAcceptBest(arg: { fallback: string }): void { if (!this._editor.hasModel()) { return; @@ -312,7 +313,7 @@ export class SuggestController implements IEditorContribution { const fallback = () => { if (positionNow.equals(this._editor.getPosition()!)) { - this._editor.trigger('suggest', Handler.Type, { text: defaultTypeText }); + this._commandService.executeCommand(arg.fallback); } }; @@ -611,7 +612,10 @@ registerEditorCommand(new SuggestCommand({ SuggestAlternatives.OtherSuggestions.toNegated(), SnippetController2.InSnippetMode.toNegated() ), - handler: x => x.triggerSuggestAndAcceptBest('\t'),//todo@joh fallback/default configurable? + handler: (x, arg) => { + + x.triggerSuggestAndAcceptBest(isObject(arg) ? { fallback: 'tab', ...arg } : { fallback: 'tab' }); + }, kbOpts: { weight, primary: KeyCode.Tab From 232006c427c19210c9cc71c44bed6e70a7012f71 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 17:03:15 -0800 Subject: [PATCH 0455/1066] use electron openExternal, return boolean-promise, rename to 'open', #66741 --- src/vs/vscode.proposed.d.ts | 2 +- .../api/electron-browser/mainThreadWindow.ts | 11 ++++------- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 467f24620cf..812a9e33d25 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -22,7 +22,7 @@ declare module 'vscode' { /** * */ - export function openItem(uri: Uri): void; + export function open(uri: Uri): Thenable; } //#endregion diff --git a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts index 17680b7bbfe..bea9420e6a6 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWindow.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWindow.ts @@ -6,8 +6,7 @@ import { Event } from 'vs/base/common/event'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; import { ExtHostContext, ExtHostWindowShape, IExtHostContext, MainContext, MainThreadWindowShape } from '../node/extHost.protocol'; @@ -20,7 +19,7 @@ export class MainThreadWindow implements MainThreadWindowShape { constructor( extHostContext: IExtHostContext, @IWindowService private readonly windowService: IWindowService, - @IOpenerService private readonly openerService: IOpenerService, + @IWindowsService private readonly windowsService: IWindowsService ) { this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostWindow); @@ -36,9 +35,7 @@ export class MainThreadWindow implements MainThreadWindowShape { return this.windowService.isFocused(); } - $openUri(uri: UriComponents): Promise { - // todo@joh turn this around and let the command work with - // the proper implementation - return this.openerService.open(URI.revive(uri)).then(() => undefined); + $openUri(uri: UriComponents): Promise { + return this.windowsService.openExternal(URI.revive(uri).toString(true)); } } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 86fc24670b2..c2a9c874792 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -256,7 +256,7 @@ export function createApiFactory( get clipboard(): vscode.Clipboard { return extHostClipboard; }, - openItem: proposedApiFunction(extension, (uri: URI) => { + open: proposedApiFunction(extension, (uri: URI) => { return extHostWindow.openUri(uri); }) }); diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b39e87c00c5..b1dd94d4784 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -621,7 +621,7 @@ export interface MainThreadDebugServiceShape extends IDisposable { export interface MainThreadWindowShape extends IDisposable { $getWindowVisibility(): Promise; - $openUri(uri: UriComponents): Promise; + $openUri(uri: UriComponents): Promise; } // -- extension host From ea59a710ca419d66063592b9efd97b0e361676b5 Mon Sep 17 00:00:00 2001 From: Romain Marcadier-Muller Date: Mon, 21 Jan 2019 16:22:36 -0800 Subject: [PATCH 0456/1066] 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 4f8d546aa7b0f18f7eabdc4eefe627558364f785 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 16:02:17 -0800 Subject: [PATCH 0457/1066] De-duplicate "fix all" quick fixes across requests for multiple diagnostics in selection range For https://github.com/Microsoft/typescript-tslint-plugin/issues/49 --- .../src/features/quickFix.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/typescript-language-features/src/features/quickFix.ts b/extensions/typescript-language-features/src/features/quickFix.ts index 16b2069c232..ec98820c7f5 100644 --- a/extensions/typescript-language-features/src/features/quickFix.ts +++ b/extensions/typescript-language-features/src/features/quickFix.ts @@ -128,8 +128,8 @@ class DiagnosticsSet { } class CodeActionSet { - private _actions = new Set(); - private _fixAllActions = new Map<{}, vscode.CodeAction>(); + private readonly _actions = new Set(); + private readonly _fixAllActions = new Map<{}, vscode.CodeAction>(); public get values(): Iterable { return this._actions; @@ -142,7 +142,7 @@ class CodeActionSet { public addFixAllAction(fixId: {}, action: vscode.CodeAction) { const existing = this._fixAllActions.get(fixId); if (existing) { - // reinsert action at back + // reinsert action at back of actions list this._actions.delete(existing); } this.addAction(action); @@ -216,33 +216,33 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { await this.formattingConfigurationManager.ensureConfigurationForDocument(document, token); - const results: vscode.CodeAction[] = []; + const results = new CodeActionSet(); for (const diagnostic of fixableDiagnostics.values) { - results.push(...await this.getFixesForDiagnostic(document, file, diagnostic, token)); + await this.getFixesForDiagnostic(document, file, diagnostic, results, token); } - return results; + return Array.from(results.values); } private async getFixesForDiagnostic( document: vscode.TextDocument, file: string, diagnostic: vscode.Diagnostic, - token: vscode.CancellationToken - ): Promise> { + results: CodeActionSet, + token: vscode.CancellationToken, + ): Promise { const args: Proto.CodeFixRequestArgs = { ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), errorCodes: [+(diagnostic.code!)] }; const response = await this.client.execute('getCodeFixes', args, token); if (response.type !== 'response' || !response.body) { - return []; + return results; } - const results = new CodeActionSet(); for (const tsCodeFix of response.body) { this.addAllFixesForTsCodeAction(results, document, file, diagnostic, tsCodeFix as Proto.CodeFixAction); } - return results.values; + return results; } private addAllFixesForTsCodeAction( @@ -282,7 +282,7 @@ class TypeScriptQuickFixProvider implements vscode.CodeActionProvider { diagnostic: vscode.Diagnostic, tsAction: Proto.CodeFixAction, ): CodeActionSet { - if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(results)) { + if (!tsAction.fixId || this.client.apiVersion.lt(API.v270) || results.hasFixAllAction(tsAction.fixId)) { return results; } From 4e6bd4aedd05aad489af94360d4b674ff544a3fc Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 14 Jan 2019 15:15:42 -0800 Subject: [PATCH 0458/1066] Prototype autofix source code action Part of #62110 * Adds a new `CodeActionKind`: `source.autoFix`. * Implements a simple auto fix provider for typescript. This provider can auto fix `implement interface` and `spelling` errors (provided there is only a single valid fix listed) The provider is likely not something we actually want to check it (especially in its current state), we should ask TS for proper autoFix support --- .../src/features/autoFix.ts | 133 ++++++++++++++++++ .../src/languageProvider.ts | 1 + .../src/typescriptService.ts | 2 +- .../common/config/commonEditorConfig.ts | 4 + src/vs/vscode.proposed.d.ts | 10 +- src/vs/workbench/api/node/extHostTypes.ts | 1 + 6 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 extensions/typescript-language-features/src/features/autoFix.ts diff --git a/extensions/typescript-language-features/src/features/autoFix.ts b/extensions/typescript-language-features/src/features/autoFix.ts new file mode 100644 index 00000000000..a10b31cb593 --- /dev/null +++ b/extensions/typescript-language-features/src/features/autoFix.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import * as Proto from '../protocol'; +import { ITypeScriptServiceClient } from '../typescriptService'; +import API from '../utils/api'; +import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import * as typeConverters from '../utils/typeConverters'; +import { DiagnosticsManager } from './diagnostics'; +import FileConfigurationManager from './fileConfigurationManager'; + +const localize = nls.loadMessageBundle(); + +const autoFixableDiagnosticCodes = new Set([ + 2420, // Incorrectly implemented interface + 2552, // Cannot find name +]); + +class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { + + public static readonly metadata: vscode.CodeActionProviderMetadata = { + providedCodeActionKinds: [vscode.CodeActionKind.SourceAutoFix] + }; + + constructor( + private readonly client: ITypeScriptServiceClient, + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly diagnosticsManager: DiagnosticsManager, + ) { } + + public async provideCodeActions( + document: vscode.TextDocument, + _range: vscode.Range, + context: vscode.CodeActionContext, + token: vscode.CancellationToken + ): Promise { + if (!context.only || !context.only.contains(vscode.CodeActionKind.Source)) { + return undefined; + } + + const file = this.client.toOpenedFilePath(document); + if (!file) { + return undefined; + } + + const autoFixableDiagnostics = this.getAutoFixableDiagnostics(document); + if (!autoFixableDiagnostics.length) { + return undefined; + } + + const fixAllAction = await this.getFixAllCodeAction(document, file, autoFixableDiagnostics, token); + return fixAllAction ? [fixAllAction] : undefined; + } + + private getAutoFixableDiagnostics( + document: vscode.TextDocument + ): vscode.Diagnostic[] { + if (this.client.bufferSyncSupport.hasPendingDiagnostics(document.uri)) { + return []; + } + + return this.diagnosticsManager.getDiagnostics(document.uri) + .filter(x => autoFixableDiagnosticCodes.has(x.code as number)); + } + + private async getFixAllCodeAction( + document: vscode.TextDocument, + file: string, + diagnostics: ReadonlyArray, + token: vscode.CancellationToken, + ): Promise { + await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + + const autoFixResponse = await this.getAutoFixEdit(file, diagnostics, token); + if (!autoFixResponse) { + return undefined; + } + const { edit, fixedDiagnostics } = autoFixResponse; + const codeAction = new vscode.CodeAction( + localize('autoFix.label', 'Auto fix'), + vscode.CodeActionKind.SourceAutoFix); + codeAction.edit = edit; + codeAction.diagnostics = fixedDiagnostics; + + return codeAction; + } + + private async getAutoFixEdit( + file: string, + diagnostics: ReadonlyArray, + token: vscode.CancellationToken, + ): Promise<{ edit: vscode.WorkspaceEdit, fixedDiagnostics: vscode.Diagnostic[] } | undefined> { + const edit = new vscode.WorkspaceEdit(); + const fixedDiagnostics: vscode.Diagnostic[] = []; + for (const diagnostic of diagnostics) { + const args: Proto.CodeFixRequestArgs = { + ...typeConverters.Range.toFileRangeRequestArgs(file, diagnostic.range), + errorCodes: [+(diagnostic.code!)] + }; + const response = await this.client.execute('getCodeFixes', args, token); + if (response.type !== 'response' || !response.body || response.body.length > 1) { + return undefined; + } + + const fix = response.body[0]; + if (new Set(['fixClassIncorrectlyImplementsInterface', 'spelling']).has(fix.fixName)) { + typeConverters.WorkspaceEdit.withFileCodeEdits(edit, this.client, fix.changes); + fixedDiagnostics.push(diagnostic); + } + } + + if (!fixedDiagnostics.length) { + return undefined; + } + + return { edit, fixedDiagnostics }; + } +} + +export function register( + selector: vscode.DocumentSelector, + client: ITypeScriptServiceClient, + fileConfigurationManager: FileConfigurationManager, + diagnosticsManager: DiagnosticsManager) { + return new VersionDependentRegistration(client, API.v213, () => + vscode.languages.registerCodeActionsProvider(selector, + new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), + TypeScriptAutoFixProvider.metadata)); +} diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index dc25b87ee73..beaa7b3b9f1 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -68,6 +68,7 @@ export default class LanguageProvider extends Disposable { this._register((await import('./features/jsDocCompletions')).register(selector, 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/autoFix')).register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager)); this._register((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); this._register((await import('./features/references')).register(selector, this.client)); this._register((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index eb48a67b99a..fecd18e10d6 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -37,7 +37,7 @@ export interface TypeScriptRequestTypes { 'format': [Proto.FormatRequestArgs, Proto.FormatResponse]; 'formatonkey': [Proto.FormatOnKeyRequestArgs, Proto.FormatResponse]; 'getApplicableRefactors': [Proto.GetApplicableRefactorsRequestArgs, Proto.GetApplicableRefactorsResponse]; - 'getCodeFixes': [Proto.CodeFixRequestArgs, Proto.GetCodeFixesResponse]; + 'getCodeFixes': [Proto.CodeFixRequestArgs, Proto.CodeFixResponse]; 'getCombinedCodeFix': [Proto.GetCombinedCodeFixRequestArgs, Proto.GetCombinedCodeFixResponse]; 'getEditsForFileRename': [Proto.GetEditsForFileRenameRequestArgs, Proto.GetEditsForFileRenameResponse]; 'getEditsForRefactor': [Proto.GetEditsForRefactorRequestArgs, Proto.GetEditsForRefactorResponse]; diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index f663112cf47..63b2ee793a8 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -838,6 +838,10 @@ const editorConfiguration: IConfigurationNode = { 'source.organizeImports': { 'type': 'boolean', 'description': nls.localize('codeActionsOnSave.organizeImports', "Controls whether organize imports action should be run on file save.") + }, + 'source.autoFix': { + 'type': 'boolean', + 'description': nls.localize('codeActionsOnSave.autoFix', "Controls whether auto fix action should be run on file save.") } }, 'additionalProperties': { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 812a9e33d25..4fce9d49e50 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1113,7 +1113,7 @@ declare module 'vscode' { //#region CodeAction.isPreferred - mjbvz export interface CodeAction { /** - * If the action is a prefered action or fix to take. + * 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. @@ -1123,4 +1123,12 @@ declare module 'vscode' { //#endregion + //#region Autofix - mjbvz + export namespace CodeActionKind { + /** + * Base kind for an auto fix source action: `source.autoFix`. + */ + export const SourceAutoFix: CodeActionKind; + } + //#endregion } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 90c05fd6e11..3662ff559ee 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1022,6 +1022,7 @@ export class CodeActionKind { public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite'); public static readonly Source = CodeActionKind.Empty.append('source'); public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports'); + public static readonly SourceAutoFix = CodeActionKind.Source.append('autoFix'); constructor( public readonly value: string From ee65a242a9dbf40e0ce3c93616ed78760995f15f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 17:01:07 -0800 Subject: [PATCH 0459/1066] Gate auto fix for TS to use an undocumented experimental TS setting We likely do not want to enable autofixes until we have proper TS support: https://github.com/Microsoft/TypeScript/issues/29452 --- .../src/features/autoFix.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/features/autoFix.ts b/extensions/typescript-language-features/src/features/autoFix.ts index a10b31cb593..c0d8b581774 100644 --- a/extensions/typescript-language-features/src/features/autoFix.ts +++ b/extensions/typescript-language-features/src/features/autoFix.ts @@ -8,7 +8,7 @@ import * as nls from 'vscode-nls'; import * as Proto from '../protocol'; import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; -import { VersionDependentRegistration } from '../utils/dependentRegistration'; +import { ConfigurationDependentRegistration, VersionDependentRegistration } from '../utils/dependentRegistration'; import * as typeConverters from '../utils/typeConverters'; import { DiagnosticsManager } from './diagnostics'; import FileConfigurationManager from './fileConfigurationManager'; @@ -126,8 +126,9 @@ export function register( client: ITypeScriptServiceClient, fileConfigurationManager: FileConfigurationManager, diagnosticsManager: DiagnosticsManager) { - return new VersionDependentRegistration(client, API.v213, () => - vscode.languages.registerCodeActionsProvider(selector, - new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), - TypeScriptAutoFixProvider.metadata)); + return new VersionDependentRegistration(client, API.v300, () => + new ConfigurationDependentRegistration('typescript', 'experimental.autoFix.enabled', () => + vscode.languages.registerCodeActionsProvider(selector, + new TypeScriptAutoFixProvider(client, fileConfigurationManager, diagnosticsManager), + TypeScriptAutoFixProvider.metadata))); } From 2ebdac4afd28caa05ebc6a37e491649202f8aa03 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 17:02:02 -0800 Subject: [PATCH 0460/1066] Fix potential crash when using preferred code actions --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index a6fc6fa3662..8194d0c4f07 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -100,7 +100,7 @@ export class QuickFixController implements IEditorContribution { // Or if we have a single preferred action const preferred = fixes.filter(fix => fix.isPreferred); - if (preferred.length === 0) { + if (preferred.length === 1) { this._onApplyCodeAction(preferred[0]); return; } From c994fc20fa58838efd235f74525ab8028dd386f6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 17:10:25 -0800 Subject: [PATCH 0461/1066] Apply auto fix on save actions sequentially and make sure we request fixes of the specific type we are interested in --- .../src/features/autoFix.ts | 2 +- .../src/features/organizeImports.ts | 4 +-- .../mainThreadSaveParticipant.ts | 31 ++++++++++++------- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/extensions/typescript-language-features/src/features/autoFix.ts b/extensions/typescript-language-features/src/features/autoFix.ts index c0d8b581774..7dfd96d3d23 100644 --- a/extensions/typescript-language-features/src/features/autoFix.ts +++ b/extensions/typescript-language-features/src/features/autoFix.ts @@ -38,7 +38,7 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): Promise { - if (!context.only || !context.only.contains(vscode.CodeActionKind.Source)) { + if (!context.only || !(context.only.contains(vscode.CodeActionKind.SourceAutoFix) || vscode.CodeActionKind.SourceAutoFix.contains(context.only))) { return undefined; } diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index 7a4cdf7bc46..55458284c20 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -82,14 +82,14 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi return []; } - if (!context.only || !context.only.contains(vscode.CodeActionKind.SourceOrganizeImports)) { + if (!context.only || !(context.only.contains(vscode.CodeActionKind.SourceOrganizeImports) || vscode.CodeActionKind.SourceOrganizeImports.contains(context.only))) { return []; } this.fileConfigManager.ensureConfigurationForDocument(document, token); const action = new vscode.CodeAction( - localize('oraganizeImportsAction.title', "Organize Imports"), + localize('organizeImportsAction.title', "Organize Imports"), vscode.CodeActionKind.SourceOrganizeImports); action.command = { title: '', command: OrganizeImportsCommand.Id, arguments: [file] }; return [action]; diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 15ee58b4855..3e233c987cf 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -318,13 +318,22 @@ class CodeActionOnParticipant implements ISaveParticipant { const timeout = this._configurationService.getValue('editor.codeActionsOnSaveTimeout', settingsOverrides); - return new Promise((resolve, reject) => { - setTimeout(() => reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)), timeout); - this.getActionsToRun(model, codeActionsOnSave).then(resolve); - }).then(actionsToRun => { - // Failure to apply a code action should not block other on save actions - return this.applyCodeActions(actionsToRun).catch(() => undefined); - }); + return Promise.race([ + new Promise((_resolve, reject) => + setTimeout(() => reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)), timeout)), + this.applyOnSaveActions(model, codeActionsOnSave) + ]).then(() => undefined); + } + + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: CodeActionKind[]): Promise { + for (const codeActionKind of codeActionsOnSave) { + const actionsToRun = await this.getActionsToRun(model, codeActionKind); + try { + await this.applyCodeActions(actionsToRun); + } catch { + // Failure to apply a code action should not block other on save actions + } + } } private async applyCodeActions(actionsToRun: CodeAction[]) { @@ -333,13 +342,11 @@ class CodeActionOnParticipant implements ISaveParticipant { } } - private async getActionsToRun(model: ITextModel, codeActionsOnSave: CodeActionKind[]) { - const actions = await getCodeActions(model, model.getFullModelRange(), { + private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind) { + return getCodeActions(model, model.getFullModelRange(), { type: 'auto', - filter: { kind: CodeActionKind.Source, includeSourceActions: true }, + filter: { kind: codeActionKind, includeSourceActions: true }, }); - const actionsToRun = actions.filter(returnedAction => returnedAction.kind && codeActionsOnSave.some(onSaveKind => onSaveKind.contains(returnedAction.kind))); - return actionsToRun; } } From 1d4bed79e1084df3aff80eb90b2bc3c4a5e19141 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 17:20:19 -0800 Subject: [PATCH 0462/1066] add some jsdoc #66741 --- src/vs/vscode.proposed.d.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 4fce9d49e50..7da978be049 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -19,10 +19,18 @@ 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(uri: Uri): Thenable; + export function open(target: Uri): Thenable; } //#endregion From e910e4e678ee08d1afa932ae0cab1df983491f40 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 17:39:16 -0800 Subject: [PATCH 0463/1066] OpenerService#open should return a boolean --- .../editor/browser/services/openerService.ts | 39 +++++++++++-------- src/vs/platform/opener/common/opener.ts | 2 +- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 40b76672b35..587ffb1fd09 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -23,15 +23,24 @@ export class OpenerService implements IOpenerService { // } - open(resource: URI, options?: { openToSide?: boolean }): Promise { + open(resource: URI, options?: { openToSide?: boolean }): Promise { const { scheme, path, query, fragment } = resource; - let promise: Promise | undefined = undefined; - if (scheme === Schemas.http || scheme === Schemas.https || scheme === Schemas.mailto) { + if (!scheme) { + // no scheme ?!? + return Promise.resolve(false); + + } else if (scheme === Schemas.http || scheme === Schemas.https || scheme === Schemas.mailto) { // open http or default mail application dom.windowOpenNoOpener(resource.toString(true)); - } else if (scheme === 'command' && CommandsRegistry.getCommand(path)) { + return Promise.resolve(true); + + } else if (scheme === Schemas.command) { + // run command or bail out if command isn't known + if (!CommandsRegistry.getCommand(path)) { + return Promise.reject(`command '${path}' NOT known`); + } // execute as command let args: any = []; try { @@ -42,13 +51,10 @@ export class OpenerService implements IOpenerService { } catch (e) { // } - promise = this._commandService.executeCommand(path, ...args); + return this._commandService.executeCommand(path, ...args).then(() => true); } else { - let selection: { - startLineNumber: number; - startColumn: number; - } | undefined = undefined; + let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined; const match = /^L?(\d+)(?:,(\d+))?/.exec(fragment); if (match) { // support file:///some/file.js#73,84 @@ -61,16 +67,15 @@ export class OpenerService implements IOpenerService { resource = resource.with({ fragment: '' }); } - if (!resource.scheme) { - // we cannot handle those - return Promise.resolve(undefined); - - } else if (resource.scheme === Schemas.file) { + if (resource.scheme === Schemas.file) { resource = resources.normalizePath(resource); // workaround for non-normalized paths (https://github.com/Microsoft/vscode/issues/12954) } - promise = this._editorService.openCodeEditor({ resource, options: { selection, } }, this._editorService.getFocusedCodeEditor(), options && options.openToSide); - } - return Promise.resolve(promise); + return this._editorService.openCodeEditor( + { resource, options: { selection, } }, + this._editorService.getFocusedCodeEditor(), + options && options.openToSide + ).then(() => true); + } } } diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 85075ec864a..551749c071b 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -18,7 +18,7 @@ export interface IOpenerService { * @param resource A resource * @return A promise that resolves when the opening is done. */ - open(resource: URI, options?: { openToSide?: boolean }): Promise; + open(resource: URI, options?: { openToSide?: boolean }): Promise; } export const NullOpenerService: IOpenerService = Object.freeze({ From 86bb14550bd5ea4538e5b2034436aa31de6dd481 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 17:55:24 -0800 Subject: [PATCH 0464/1066] Strict null check tokenization --- src/tsconfig.strictNullChecks.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index f801b9fe24d..bdec37c4b49 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -251,6 +251,7 @@ "./vs/editor/contrib/rename/renameInputField.ts", "./vs/editor/contrib/suggest/test/suggestMemory.test.ts", "./vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode.ts", + "./vs/editor/contrib/tokenization/tokenization.ts", "./vs/editor/contrib/wordHighlighter/wordHighlighter.ts", "./vs/editor/contrib/wordOperations/test/wordOperations.test.ts", "./vs/editor/contrib/wordOperations/test/wordTestUtils.ts", From f929531dfba807eded556763d56707d9625fb780 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 18:03:38 -0800 Subject: [PATCH 0465/1066] Add CodeActionKind.intersects Fixes #66881 --- .../src/features/autoFix.ts | 2 +- .../src/features/organizeImports.ts | 2 +- src/vs/vscode.d.ts | 15 ++++++++++++-- src/vs/workbench/api/node/extHostTypes.ts | 4 ++++ .../electron-browser/api/extHostTypes.test.ts | 20 +++++++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/features/autoFix.ts b/extensions/typescript-language-features/src/features/autoFix.ts index 7dfd96d3d23..2a9e9d1f0e7 100644 --- a/extensions/typescript-language-features/src/features/autoFix.ts +++ b/extensions/typescript-language-features/src/features/autoFix.ts @@ -38,7 +38,7 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): Promise { - if (!context.only || !(context.only.contains(vscode.CodeActionKind.SourceAutoFix) || vscode.CodeActionKind.SourceAutoFix.contains(context.only))) { + if (!context.only || !vscode.CodeActionKind.SourceAutoFix.intersects(context.only)) { return undefined; } diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index 55458284c20..58dbea70714 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -82,7 +82,7 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi return []; } - if (!context.only || !(context.only.contains(vscode.CodeActionKind.SourceOrganizeImports) || vscode.CodeActionKind.SourceOrganizeImports.contains(context.only))) { + if (!context.only || !vscode.CodeActionKind.SourceOrganizeImports.intersects(context.only)) { return []; } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 42d9d87e05e..fd07204c108 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2029,9 +2029,20 @@ declare module 'vscode' { append(parts: string): CodeActionKind; /** - * Does this kind contain `other`? + * Checks if this code action kind intersects `other`. * - * The kind `"refactor"` for example contains `"refactor.extract"` and ``"refactor.extract.function"`, but not `"unicorn.refactor.extract"` or `"refactory.extract"` + * The kind `"refactor.extract"` for example intersects `refactor`, `"refactor.extract"` and ``"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"`. + * + * @param other Kind to check. + */ + intersects(other: CodeActionKind): boolean; + + /** + * Checks if `other` is a sub-kind of this `CodeActionKind`. + * + * The kind `"refactor.extract"` for example contains `"refactor.extract"` and ``"refactor.extract.function"`, + * but not `"unicorn.refactor.extract"`, or `"refactor.extractAll"` or `refactor`. * * @param other Kind to check. */ diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 3662ff559ee..5535461e3dc 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1032,6 +1032,10 @@ export class CodeActionKind { return new CodeActionKind(this.value ? this.value + CodeActionKind.sep + parts : parts); } + public intersects(other: CodeActionKind): boolean { + return this.contains(other) || other.contains(this); + } + public contains(other: CodeActionKind): boolean { return this.value === other.value || startsWith(other.value, this.value + CodeActionKind.sep); } diff --git a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts index d2deeaf2163..30cf11ba5f5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTypes.test.ts @@ -532,4 +532,24 @@ suite('ExtHostTypes', function () { assert.ok(error instanceof Error); assert.ok(error instanceof types.FileSystemError); }); + + test('CodeActionKind contains', () => { + assert.ok(types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.RefactorExtract)); + assert.ok(types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.RefactorExtract.append('other'))); + + assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Refactor)); + assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Refactor.append('other'))); + assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Empty.append('other').append('refactor'))); + assert.ok(!types.CodeActionKind.RefactorExtract.contains(types.CodeActionKind.Empty.append('refactory'))); + }); + + test('CodeActionKind intersects', () => { + assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.RefactorExtract)); + assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Refactor)); + assert.ok(types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.RefactorExtract.append('other'))); + + assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Refactor.append('other'))); + assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Empty.append('other').append('refactor'))); + assert.ok(!types.CodeActionKind.RefactorExtract.intersects(types.CodeActionKind.Empty.append('refactory'))); + }); }); From 65b0d6d274ef74d9c56e741e673cb47f130b7826 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 21 Jan 2019 18:03:52 -0800 Subject: [PATCH 0466/1066] Fix spelling error --- src/vs/workbench/api/node/extHostTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 5535461e3dc..803950badb5 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1000,7 +1000,7 @@ export class CodeAction { edit?: WorkspaceEdit; - dianostics?: Diagnostic[]; + diagnostics?: Diagnostic[]; kind?: CodeActionKind; From 97ed8aba2953cfc5089fefa23ef65c272070287e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 18:18:52 -0800 Subject: [PATCH 0467/1066] add developer command that prints available formatters to console, #41882 --- src/vs/editor/common/modes.ts | 6 ++ src/vs/editor/contrib/format/formatActions.ts | 70 ++++++++++++++----- src/vs/monaco.d.ts | 2 + .../mainThreadLanguageFeatures.ts | 8 ++- src/vs/workbench/api/node/extHost.protocol.ts | 6 +- .../api/node/extHostLanguageFeatures.ts | 12 ++-- 6 files changed, 77 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 3e6e7bcc4cd..ae8a3b77861 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -913,6 +913,9 @@ export interface FormattingOptions { * the formatting-feature. */ export interface DocumentFormattingEditProvider { + + displayName?: string; + /** * Provide formatting edits for a whole document. */ @@ -923,6 +926,9 @@ export interface DocumentFormattingEditProvider { * the formatting-feature. */ export interface DocumentRangeFormattingEditProvider { + + displayName?: string; + /** * Provide formatting edits for a range in a document. * diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 3d9b801958c..bc44a9eb5cd 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -3,31 +3,32 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; +import { alert } from 'vs/base/browser/ui/aria/aria'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, DocumentFormattingEditProviderRegistry, FormattingOptions } from 'vs/editor/common/modes'; -import { getOnTypeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format'; -import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { sequence } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState'; +import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorAction, registerEditorAction, registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { CharacterSet } from 'vs/editor/common/core/characterClassifier'; import { Range } from 'vs/editor/common/core/range'; -import { alert } from 'vs/base/browser/ui/aria/aria'; -import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ISingleEditOperation } from 'vs/editor/common/model'; -import { INotificationService } from 'vs/platform/notification/common/notification'; +import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { getOnTypeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format'; +import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; +import * as nls from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { sequence } from 'vs/base/common/async'; +import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { MenuRegistry } from 'vs/platform/actions/common/actions'; function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -484,3 +485,38 @@ CommandsRegistry.registerCommand('editor.action.format', accessor => { return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Selection, { tabSize, insertSpaces }, CancellationToken.None); } }); + + +CommandsRegistry.registerCommand('editor.action.formatInspect', accessor => { + + const editor = accessor.get(ICodeEditorService).getActiveCodeEditor(); + if (!editor || !editor.hasModel()) { + return undefined; + } + console.log(`Available Formatters for: ${editor.getModel().uri.toString(true)}`); + // range formatters + const documentRangeProvider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()); + console.group('Range Formatters'); + if (documentRangeProvider.length === 0) { + console.log('none'); + } else { + documentRangeProvider.forEach(value => console.log(value.displayName)); + } + console.groupEnd(); + + // whole document formatters + const documentProvider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel()); + console.group('Document Formatters'); + if (documentProvider.length === 0) { + console.log('none'); + } else { + documentProvider.forEach(value => console.log(value.displayName)); + } + console.groupEnd(); +}); + +MenuRegistry.addCommand({ + id: 'editor.action.formatInspect', + category: nls.localize('cat', "Developer"), + title: nls.localize('title', "Print Available Formatters..."), +}); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index a15aadccd86..22e332b8821 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5166,6 +5166,7 @@ declare namespace monaco.languages { * the formatting-feature. */ export interface DocumentFormattingEditProvider { + displayName?: string; /** * Provide formatting edits for a whole document. */ @@ -5177,6 +5178,7 @@ declare namespace monaco.languages { * the formatting-feature. */ export interface DocumentRangeFormattingEditProvider { + displayName?: string; /** * Provide formatting edits for a range in a document. * diff --git a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts index eeec73908cf..bdd181a2cac 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadLanguageFeatures.ts @@ -108,7 +108,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- outline - $registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void { + $registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void { this._registrations[handle] = modes.DocumentSymbolProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { displayName, provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise => { @@ -223,16 +223,18 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha // --- formatting - $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void { + $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void { this._registrations[handle] = modes.DocumentFormattingEditProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { + displayName, provideDocumentFormattingEdits: (model: ITextModel, options: modes.FormattingOptions, token: CancellationToken): Promise => { return this._proxy.$provideDocumentFormattingEdits(handle, model.uri, options, token); } }); } - $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void { + $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], displayName: string): void { this._registrations[handle] = modes.DocumentRangeFormattingEditProviderRegistry.register(typeConverters.LanguageSelector.from(selector), { + displayName, provideDocumentRangeFormattingEdits: (model: ITextModel, range: EditorRange, options: modes.FormattingOptions, token: CancellationToken): Promise => { return this._proxy.$provideDocumentRangeFormattingEdits(handle, model.uri, range, options, token); } diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index b1dd94d4784..da87c79aaf5 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -286,7 +286,7 @@ export interface ISerializedSignatureHelpProviderMetadata { export interface MainThreadLanguageFeaturesShape extends IDisposable { $unregister(handle: number): void; - $registerOutlineSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void; + $registerDocumentSymbolProvider(handle: number, selector: ISerializedDocumentFilter[], label: string): void; $registerCodeLensSupport(handle: number, selector: ISerializedDocumentFilter[], eventHandle: number): void; $emitCodeLensEvent(eventHandle: number, event?: any): void; $registerDefinitionSupport(handle: number, selector: ISerializedDocumentFilter[]): void; @@ -297,8 +297,8 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $registerDocumentHighlightProvider(handle: number, selector: ISerializedDocumentFilter[]): void; $registerReferenceSupport(handle: number, selector: ISerializedDocumentFilter[]): void; $registerQuickFixSupport(handle: number, selector: ISerializedDocumentFilter[], supportedKinds?: string[]): void; - $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void; - $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[]): void; + $registerDocumentFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void; + $registerRangeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], label: string): void; $registerOnTypeFormattingSupport(handle: number, selector: ISerializedDocumentFilter[], autoFormatTriggerCharacters: string[]): void; $registerNavigateTypeSupport(handle: number): void; $registerRenameSupport(handle: number, selector: ISerializedDocumentFilter[], supportsResolveInitialValues: boolean): void; diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index b8b1d17ff4c..8379fa3ad79 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -1018,12 +1018,16 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { return handle; } + private static _extLabel(ext: IExtensionDescription): string { + return ext.displayName || ext.name; + } + // --- outline registerDocumentSymbolProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentSymbolProvider, metadata?: vscode.DocumentSymbolProviderMetadata): vscode.Disposable { const handle = this._addNewAdapter(new DocumentSymbolAdapter(this._documents, provider), extension); - const displayName = (metadata && metadata.label) || (extension && (extension.displayName || extension.name)) || undefined; - this._proxy.$registerOutlineSupport(handle, this._transformDocumentSelector(selector), displayName); + const displayName = (metadata && metadata.label) || ExtHostLanguageFeatures._extLabel(extension); + this._proxy.$registerDocumentSymbolProvider(handle, this._transformDocumentSelector(selector), displayName); return this._createDisposable(handle); } @@ -1152,7 +1156,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerDocumentFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentFormattingEditProvider): vscode.Disposable { const handle = this._addNewAdapter(new DocumentFormattingAdapter(this._documents, provider), extension); - this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector)); + this._proxy.$registerDocumentFormattingSupport(handle, this._transformDocumentSelector(selector), ExtHostLanguageFeatures._extLabel(extension)); return this._createDisposable(handle); } @@ -1162,7 +1166,7 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { registerDocumentRangeFormattingEditProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.DocumentRangeFormattingEditProvider): vscode.Disposable { const handle = this._addNewAdapter(new RangeFormattingAdapter(this._documents, provider), extension); - this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector)); + this._proxy.$registerRangeFormattingSupport(handle, this._transformDocumentSelector(selector), ExtHostLanguageFeatures._extLabel(extension)); return this._createDisposable(handle); } From 9faa15ed6fa5db013bed38a7a2e17de259d221bb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 19:39:21 -0800 Subject: [PATCH 0468/1066] fix strict null errors --- src/vs/editor/contrib/format/formatActions.ts | 2 +- src/vs/platform/opener/common/opener.ts | 2 +- .../parts/snippets/electron-browser/configureSnippets.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index bc44a9eb5cd..6cbb2a2aeb9 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -491,7 +491,7 @@ CommandsRegistry.registerCommand('editor.action.formatInspect', accessor => { const editor = accessor.get(ICodeEditorService).getActiveCodeEditor(); if (!editor || !editor.hasModel()) { - return undefined; + return; } console.log(`Available Formatters for: ${editor.getModel().uri.toString(true)}`); // range formatters diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index 551749c071b..043c4e392e8 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -23,5 +23,5 @@ export interface IOpenerService { export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, - open() { return Promise.resolve(undefined); } + open() { return Promise.resolve(false); } }); diff --git a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts index 5a6d39efa8e..a545024ae54 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/configureSnippets.ts @@ -187,7 +187,7 @@ async function createLanguageSnippetFile(pick: ISnippetPick, fileService: IFileS await fileService.updateContent(URI.file(pick.filepath), contents); } -CommandsRegistry.registerCommand(id, async accessor => { +CommandsRegistry.registerCommand(id, async (accessor): Promise => { const snippetService = accessor.get(ISnippetsService); const quickInputService = accessor.get(IQuickInputService); From 23f534b7b7f439fc39755821ff99ac50fcc10765 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 21 Jan 2019 21:07:57 -0800 Subject: [PATCH 0469/1066] fix tests --- src/vs/editor/test/browser/services/openerService.test.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index 9222455b70e..f5f29f651dd 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -79,12 +79,6 @@ suite('OpenerService', function () { const openerService = new OpenerService(editorService, commandService); - // unknown command - openerService.open(URI.parse('command:foobar')); - assert.equal(lastCommand, undefined); - assert.equal(editorService.lastInput.resource.toString(), 'command:foobar'); - assert.equal(editorService.lastInput.options!.selection, undefined); - const id = `aCommand${Math.random()}`; CommandsRegistry.registerCommand(id, function () { }); From 82aab5a16ebd44eaaaaaf1396f82430b83723108 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Mon, 21 Jan 2019 22:15:39 -0800 Subject: [PATCH 0470/1066] 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 b29504c5c2c8dd19c769229331f38ff034b069b7 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 22 Jan 2019 11:00:28 +0100 Subject: [PATCH 0471/1066] #66907 Move cached configurations to user home directory --- src/vs/workbench/services/configuration/node/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 338a3ddc1f7..673cf4ed0da 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -370,7 +370,7 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf configFolderRelativePath: string, environmentService: IEnvironmentService) { super(); - this.cachedFolderPath = paths.join(environmentService.appSettingsHome, createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex')); + this.cachedFolderPath = paths.join(environmentService.userDataPath, 'CachedConfigurations', createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex')); this.cachedConfigurationPath = paths.join(this.cachedFolderPath, 'configuration.json'); this.configurationModel = new ConfigurationModel(); } From 93b0145d37aa36af1f7cf1eee2e20a0bf5f6b4f7 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 22 Jan 2019 11:58:48 +0100 Subject: [PATCH 0472/1066] explorer: do not react on selection events if user is modifying selection via shift fixes #66792 --- src/vs/workbench/electron-browser/commands.ts | 2 +- .../parts/files/electron-browser/views/explorerView.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index 2123b8bb2d6..fcc3b93ce06 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -115,7 +115,7 @@ export function registerCommands(): void { const focus = list.getFocus() ? list.getFocus()[0] : undefined; const selection = list.getSelection(); - const fakeKeyboardEvent = new KeyboardEvent('keydown'); + const fakeKeyboardEvent = new KeyboardEvent('keydown', { shiftKey: true }); if (selection && selection.indexOf(focus) >= 0) { list.setSelection(selection.filter(s => s !== previousFocus), fakeKeyboardEvent); 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 ee4d8910b65..c9e31ba5f41 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -64,7 +64,6 @@ export class ExplorerView extends ViewletPanel { private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; private ignoreActiveEditorChange; - private previousSelection: ExplorerItem[] = []; constructor( options: IViewletPanelOptions, @@ -294,9 +293,9 @@ export class ExplorerView extends ViewletPanel { const selection = e.elements; // Do not react if the user is expanding selection via keyboard. // Check if the item was previously also selected, if yes the user is simply expanding / collapsing current selection #66589. - const wasMultiSelectedAndKeyboard = (e.browserEvent instanceof KeyboardEvent) && this.previousSelection.length > 1 && this.previousSelection.indexOf(selection[0]) >= 0; - this.previousSelection = selection; - if (selection.length === 1 && !wasMultiSelectedAndKeyboard) { + + const shiftDown = e.browserEvent instanceof KeyboardEvent && e.browserEvent.shiftKey; + if (selection.length === 1 && !shiftDown) { // Do not react if user is clicking on explorer items which are input placeholders if (!selection[0].name) { // Do not react if user is clicking on explorer items which are input placeholders From 55951f06fa2f520b5e916869bc7fd4e7977a57ea Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 22 Jan 2019 14:51:01 +0100 Subject: [PATCH 0473/1066] fixes #66810 --- .../parts/files/electron-browser/media/explorerviewlet.css | 7 +------ .../parts/files/electron-browser/views/explorerViewer.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) 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 a3a6b3451ec..7e059485bbf 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -125,12 +125,7 @@ height: 22px; } -.explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { - padding: 1px 2px; -} - -.linux > .monaco-workbench .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input, -.mac > .monaco-workbench .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { +.monaco-workbench .explorer-viewlet .explorer-item .monaco-inputbox > .wrapper > .input { padding: 0; height: 20px; } 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 e8a55cb5455..d2b763b4890 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -204,8 +204,8 @@ export class FilesRenderer implements ITreeRenderer 0 && !stat.isDirectory ? lastDot : value.length }); inputBox.focus(); + inputBox.select({ start: 0, end: lastDot > 0 && !stat.isDirectory ? lastDot : value.length }); const done = once(async (success: boolean, blur: boolean) => { label.element.style.display = 'none'; From cf67606c1f0a84807addbb84fb89b962e97e1961 Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 22 Jan 2019 15:35:57 +0100 Subject: [PATCH 0474/1066] open editors: drag to outside of vscode fixes #66747 --- .../electron-browser/views/openEditorsView.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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 be88f381cb9..59efa778648 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -34,12 +34,13 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { DirtyEditorContext, OpenEditorsGroupContext } from 'vs/workbench/parts/files/electron-browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { ResourcesDropHandler } from 'vs/workbench/browser/dnd'; +import { ResourcesDropHandler, fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; import { DesktopDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { URI } from 'vs/base/common/uri'; const $ = dom.$; @@ -620,6 +621,23 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop).elements; + const resources: URI[] = []; + if (items) { + items.forEach(i => { + if (i instanceof OpenEditor) { + resources.push(i.getResource()); + } + }); + } + + if (resources.length) { + // Apply some datatransfer types to allow for dragging the element outside of the application + this.instantiationService.invokeFunction(fillResourceDataTransfers, resources, originalEvent); + } + } + onDragOver(data: IDragAndDropData, targetElement: OpenEditor | IEditorGroup, targetIndex: number, originalEvent: DragEvent): boolean | IListDragOverReaction { return true; } From 6ecf3499677577021dfdbd5c45a8fa85acf0ce07 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 22 Jan 2019 15:39:42 +0100 Subject: [PATCH 0475/1066] Make color/icon theme contribution points dynamic --- src/tsconfig.strictNullChecks.json | 3 + .../common/configurationRegistry.ts | 6 +- .../common/jsonContributionRegistry.ts | 2 +- src/vs/workbench/electron-browser/window.ts | 2 +- .../common/preferencesContribution.ts | 1 - .../electron-browser/themes.contribution.ts | 4 +- .../themes/common/workbenchThemeService.ts | 6 +- .../themes/electron-browser/colorThemeData.ts | 20 +-- .../electron-browser/colorThemeStore.ts | 12 +- .../electron-browser/fileIconThemeData.ts | 8 +- .../electron-browser/fileIconThemeStore.ts | 16 +- .../electron-browser/workbenchThemeService.ts | 148 ++++++++++-------- 12 files changed, 130 insertions(+), 98 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index bdec37c4b49..52c7d2073bd 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -881,8 +881,11 @@ "./vs/workbench/services/themes/common/colorThemeSchema.ts", "./vs/workbench/services/themes/common/fileIconThemeSchema.ts", "./vs/workbench/services/themes/common/workbenchThemeService.ts", + "./vs/workbench/services/themes/electron-browser/colorThemeData.ts", + "./vs/workbench/services/themes/electron-browser/colorThemeStore.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeData.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts", + "./vs/workbench/services/themes/electron-browser/workbenchThemeService.ts", "./vs/workbench/services/themes/electron-browser/themeCompatibility.ts", "./vs/workbench/services/timer/electron-browser/timerService.ts", "./vs/workbench/services/title/common/titleService.ts", diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 981fe710fd8..2f787370ebe 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -47,7 +47,7 @@ export interface IConfigurationRegistry { * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values. * Property or default value changes are not allowed. */ - notifyConfigurationSchemaUpdated(configuration: IConfigurationNode): void; + notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]): void; /** * Event that fires whenver a configuration has been @@ -251,8 +251,8 @@ class ConfigurationRegistry implements IConfigurationRegistry { this._onDidUpdateConfiguration.fire(properties); } - public notifyConfigurationSchemaUpdated(configuration: IConfigurationNode) { - contributionRegistry.notifySchemaChanged(editorConfigurationSchemaId); + public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) { + this._onDidSchemaChange.fire(); } public registerOverrideIdentifiers(overrideIdentifiers: string[]): void { diff --git a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts index 2f173cd9838..3206c7ba3de 100644 --- a/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts +++ b/src/vs/platform/jsonschemas/common/jsonContributionRegistry.ts @@ -26,7 +26,7 @@ export interface IJSONContributionRegistry { /** - * Notifies all listeneres that the content of the given schema has changed. + * Notifies all listeners that the content of the given schema has changed. * @param uri The id of the schema */ notifySchemaChanged(uri: string): void; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index ca737ce38f0..c0aff6bd4ba 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -180,7 +180,7 @@ export class ElectronWindow extends Themable { const windowConfig = this.configurationService.getValue('window'); if (windowConfig && windowConfig.autoDetectHighContrast) { this.lifecycleService.when(LifecyclePhase.Ready).then(() => { - this.themeService.setColorTheme(VS_HC_THEME, null); + this.themeService.setColorTheme(VS_HC_THEME, undefined); }); } }); diff --git a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts index 32a83b3b36f..5a5854a41c1 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesContribution.ts @@ -129,7 +129,6 @@ export class PreferencesContribution implements IWorkbenchContribution { const modelContent = JSON.stringify(schema); const languageSelection = this.modeService.create('jsonc'); const model = this.modelService.createModel(modelContent, languageSelection, uri); - const disposables: IDisposable[] = []; disposables.push(schemaRegistry.onDidChangeSchema(schemaUri => { if (schemaUri === uri.toString()) { diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 9a7177130d5..3acacb44791 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -60,7 +60,7 @@ export class SelectColorThemeAction extends Action { } theme = currentTheme; } - let target: ConfigurationTarget | null = null; + let target: ConfigurationTarget | undefined = undefined; if (applyTheme) { let confValue = this.configurationService.inspect(COLOR_THEME_SETTING); target = typeof confValue.workspace !== 'undefined' ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; @@ -69,7 +69,7 @@ export class SelectColorThemeAction extends Action { this.themeService.setColorTheme(theme.id, target).then(undefined, err => { onUnexpectedError(err); - this.themeService.setColorTheme(currentTheme.id, null); + this.themeService.setColorTheme(currentTheme.id, undefined); } ); }; diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 401d69a8dca..2714e963216 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -41,7 +41,7 @@ export interface IColorMap { export interface IFileIconTheme extends IIconTheme { readonly id: string; readonly label: string; - readonly settingsId?: string; + readonly settingsId: string | null; readonly description?: string; readonly extensionData?: ExtensionData; @@ -53,13 +53,13 @@ export interface IFileIconTheme extends IIconTheme { export interface IWorkbenchThemeService extends IThemeService { _serviceBrand: any; - setColorTheme(themeId: string, settingsTarget: ConfigurationTarget | null): Promise; + setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getColorTheme(): IColorTheme; getColorThemes(): Promise; onDidColorThemeChange: Event; restoreColorTheme(); - setFileIconTheme(iconThemeId: string, settingsTarget: ConfigurationTarget | undefined): Promise; + setFileIconTheme(iconThemeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise; getFileIconTheme(): IFileIconTheme; getFileIconThemes(): Promise; onDidFileIconThemeChange: Event; diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts index b7a22a48890..1af44a2d51d 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeData.ts @@ -57,8 +57,8 @@ export class ColorThemeData implements IColorTheme { private colorMap: IColorMap = {}; private customColorMap: IColorMap = {}; - public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color { - let color = this.customColorMap[colorId]; + public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | null { + let color: Color | null = this.customColorMap[colorId]; if (color) { return color; } @@ -69,7 +69,7 @@ export class ColorThemeData implements IColorTheme { return color; } - public getDefault(colorId: ColorIdentifier): Color { + public getDefault(colorId: ColorIdentifier): Color | null { return colorRegistry.resolveDefaultColor(colorId, this); } @@ -213,7 +213,7 @@ export class ColorThemeData implements IColorTheme { let themeData = new ColorThemeData(); themeData.id = id; themeData.label = ''; - themeData.settingsId = null; + themeData.settingsId = '__' + id; themeData.isLoaded = false; themeData.themeTokenColors = [{ settings: {} }]; themeData.watch = false; @@ -231,7 +231,7 @@ export class ColorThemeData implements IColorTheme { return themeData; } - static fromStorageData(input: string): ColorThemeData { + static fromStorageData(input: string): ColorThemeData | undefined { try { let data = JSON.parse(input); let theme = new ColorThemeData(); @@ -251,7 +251,7 @@ export class ColorThemeData implements IColorTheme { } return theme; } catch (e) { - return null; + return undefined; } } @@ -292,7 +292,7 @@ function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRu } let includeCompletes: Promise = Promise.resolve(null); if (contentValue.include) { - includeCompletes = _loadColorTheme(fileService, resources.joinPath(resources.dirname(themeLocation), contentValue.include), resultRules, resultColors); + includeCompletes = _loadColorTheme(fileService, resources.joinPath(resources.dirname(themeLocation)!, contentValue.include), resultRules, resultColors); } return includeCompletes.then(_ => { if (Array.isArray(contentValue.settings)) { @@ -318,7 +318,7 @@ function _loadColorTheme(fileService: IFileService, themeLocation: URI, resultRu resultRules.push(...tokenColors); return null; } else if (typeof tokenColors === 'string') { - return _loadSyntaxTokens(fileService, resources.joinPath(resources.dirname(themeLocation), tokenColors), resultRules, {}); + return _loadSyntaxTokens(fileService, resources.joinPath(resources.dirname(themeLocation)!, tokenColors), resultRules, {}); } else { return Promise.reject(new Error(nls.localize({ key: 'error.invalidformat.tokenColors', comment: ['{0} will be replaced by a path. Values in quotes should not be translated.'] }, "Problem parsing color theme file: {0}. Property 'tokenColors' should be either an array specifying colors or a path to a TextMate theme file", themeLocation.toString()))); } @@ -360,8 +360,8 @@ function _loadSyntaxTokens(fileService: IFileService, themeLocation: URI, result } function updateDefaultRuleSettings(defaultRule: ITokenColorizationRule, theme: ColorThemeData): ITokenColorizationRule { - const foreground = theme.getColor(editorForeground) || theme.getDefault(editorForeground); - const background = theme.getColor(editorBackground) || theme.getDefault(editorBackground); + const foreground = theme.getColor(editorForeground) || theme.getDefault(editorForeground)!; + const background = theme.getColor(editorBackground) || theme.getDefault(editorBackground)!; defaultRule.settings.foreground = Color.Format.CSS.formatHexA(foreground); defaultRule.settings.background = Color.Format.CSS.formatHexA(background); return defaultRule; diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 6e9386b91b9..641dc73a13c 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -16,6 +16,7 @@ import { URI } from 'vs/base/common/uri'; const themesExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'themes', + isDynamic: true, jsonSchema: { description: nls.localize('vscode.extension.contributes.themes', 'Contributes textmate color themes.'), type: 'array', @@ -61,6 +62,7 @@ export class ColorThemeStore { private initialize() { themesExtPoint.setHandler((extensions) => { + this.extensionsColorThemes.length = 1; // remove all but the default theme for (let ext of extensions) { let extensionData = { extensionId: ext.description.identifier.value, @@ -108,9 +110,9 @@ export class ColorThemeStore { }); } - public findThemeData(themeId: string, defaultId?: string): Promise { + public findThemeData(themeId: string, defaultId?: string): Promise { return this.getColorThemes().then(allThemes => { - let defaultTheme: ColorThemeData = undefined; + let defaultTheme: ColorThemeData | undefined = undefined; for (let t of allThemes) { if (t.id === themeId) { return t; @@ -123,9 +125,9 @@ export class ColorThemeStore { }); } - public findThemeDataBySettingsId(settingsId: string, defaultId: string): Promise { + public findThemeDataBySettingsId(settingsId: string, defaultId: string | undefined): Promise { return this.getColorThemes().then(allThemes => { - let defaultTheme: ColorThemeData = undefined; + let defaultTheme: ColorThemeData | undefined = undefined; for (let t of allThemes) { if (t.settingsId === settingsId) { return t; @@ -139,7 +141,7 @@ export class ColorThemeStore { } public getColorThemes(): Promise { - return this.extensionService.whenInstalledExtensionsRegistered().then(isReady => { + return this.extensionService.whenInstalledExtensionsRegistered().then(_ => { return this.extensionsColorThemes; }); } diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 1269a1bfaed..12655d450e1 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -15,7 +15,7 @@ import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages'; export class FileIconThemeData implements IFileIconTheme { id: string; label: string; - settingsId?: string; + settingsId: string | null; description?: string; hasFileIcons: boolean; hasFolderIcons: boolean; @@ -73,7 +73,7 @@ export class FileIconThemeData implements IFileIconTheme { themeData = FileIconThemeData._noIconTheme = new FileIconThemeData(); themeData.id = ''; themeData.label = ''; - themeData.settingsId = undefined; + themeData.settingsId = null; themeData.hasFileIcons = false; themeData.hasFolderIcons = false; themeData.hidesExplorerArrows = false; @@ -86,9 +86,9 @@ export class FileIconThemeData implements IFileIconTheme { static createUnloadedTheme(id: string): FileIconThemeData { let themeData = new FileIconThemeData(); - themeData.id = ''; + themeData.id = id; themeData.label = ''; - themeData.settingsId = undefined; + themeData.settingsId = '__' + id; themeData.isLoaded = false; themeData.hasFileIcons = false; themeData.hasFolderIcons = false; diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 2ec22cfc555..39d83bf5ef7 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -16,6 +16,7 @@ import { URI } from 'vs/base/common/uri'; const iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'iconThemes', + isDynamic: true, jsonSchema: { description: nls.localize('vscode.extension.contributes.iconThemes', 'Contributes file icon themes.'), type: 'array', @@ -56,6 +57,7 @@ export class FileIconThemeStore { private initialize() { iconThemeExtPoint.setHandler((extensions) => { + this.knownIconThemes.length = 0; for (let ext of extensions) { let extensionData = { extensionId: ext.description.identifier.value, @@ -109,25 +111,31 @@ export class FileIconThemeStore { } - public findThemeData(iconTheme: string): Promise { + public findThemeData(iconTheme: string): Promise { + if (iconTheme.length === 0) { + return Promise.resolve(FileIconThemeData.noIconTheme()); + } return this.getFileIconThemes().then(allIconSets => { for (let iconSet of allIconSets) { if (iconSet.id === iconTheme) { return iconSet; } } - return null; + return undefined; }); } - public findThemeBySettingsId(settingsId: string): Promise { + public findThemeBySettingsId(settingsId: string | null): Promise { + if (!settingsId) { + return Promise.resolve(FileIconThemeData.noIconTheme()); + } return this.getFileIconThemes().then(allIconSets => { for (let iconSet of allIconSets) { if (iconSet.settingsId === settingsId) { return iconSet; } } - return null; + return undefined; }); } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index 73235fecc2f..fb7c0971fd0 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -29,6 +29,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; // implementation @@ -74,12 +75,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { private currentColorTheme: ColorThemeData; private container: HTMLElement; private readonly onColorThemeChange: Emitter; - private watchedColorThemeLocation: URI; + private watchedColorThemeLocation: URI | undefined; private iconThemeStore: FileIconThemeStore; private currentIconTheme: FileIconThemeData; private readonly onFileIconThemeChange: Emitter; - private watchedIconThemeLocation: URI; + private watchedIconThemeLocation: URI | undefined; private themingParticipantChangeListener: IDisposable; private _configurationWriter: ConfigurationWriter; @@ -114,7 +115,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // In order to avoid paint flashing for tokens, because // themes are loaded asynchronously, we need to initialize // a color theme document with good defaults until the theme is loaded - let themeData: ColorThemeData | null = null; + let themeData: ColorThemeData | undefined = undefined; let persistedThemeData = this.storageService.get(PERSISTED_THEME_STORAGE_KEY, StorageScope.GLOBAL); if (persistedThemeData) { themeData = ColorThemeData.fromStorageData(persistedThemeData); @@ -126,12 +127,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeData.setCustomColors(this.colorCustomizations); themeData.setCustomTokenColors(this.tokenColorCustomizations); this.updateDynamicCSSRules(themeData); - this.applyTheme(themeData, null, true); + this.applyTheme(themeData, undefined, true); - let iconData: FileIconThemeData | null = null; let persistedIconThemeData = this.storageService.get(PERSISTED_ICON_THEME_STORAGE_KEY, StorageScope.GLOBAL); if (persistedIconThemeData) { - iconData = FileIconThemeData.fromStorageData(persistedIconThemeData); + const iconData = FileIconThemeData.fromStorageData(persistedIconThemeData); if (iconData) { _applyIconTheme(iconData, () => { this.doSetFileIconTheme(iconData); @@ -144,33 +144,48 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.installConfigurationListener(); }); - // update settings schema setting + // update settings schema setting for theme specific settings this.colorThemeStore.onDidChange(themes => { - const enumDescription = themeData.description || ''; + const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; + const themeSpecificTokenColors: IJSONSchema = { properties: {} }; - colorCustomizationsSchema.properties = colorThemeSchema.colorsSchema.properties; - const copyColorCustomizationsSchema = { ...colorCustomizationsSchema }; - copyColorCustomizationsSchema.properties = { ...colorThemeSchema.colorsSchema.properties }; - - customEditorColorSchema.properties = customEditorColorConfigurationProperties; - const copyCustomEditorColorSchema = { ...customEditorColorSchema }; - copyCustomEditorColorSchema.properties = { ...customEditorColorConfigurationProperties }; - - themes.forEach(t => { - colorThemeSettingSchema.enum.push(t.settingsId); - colorThemeSettingSchema.enumDescriptions.push(enumDescription); + for (let t of themes) { + colorThemeSettingSchema.enum!.push(t.settingsId); + colorThemeSettingSchema.enumDescriptions!.push(t.description || ''); const themeId = `[${t.settingsId}]`; - colorCustomizationsSchema.properties[themeId] = copyColorCustomizationsSchema; - customEditorColorSchema.properties[themeId] = copyCustomEditorColorSchema; - }); + themeSpecificWorkbenchColors.properties![themeId] = colorThemeSchema.colorsSchema; + themeSpecificTokenColors.properties![themeId] = { properties: tokenColorConfigurationProperties, additionalProperties: false }; + } - configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); - configurationRegistry.notifyConfigurationSchemaUpdated(customEditorColorConfiguration); + colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors; + tokenColorCustomizationSchema.allOf![1] = themeSpecificTokenColors; + + configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); + + if (this.currentColorTheme.isLoaded) { + this.colorThemeStore.findThemeData(this.currentColorTheme.id).then(theme => { + if (!theme) { + this.setColorTheme(DEFAULT_THEME_ID, undefined); + } else { + this.restoreColorTheme(); + } + }); + } }); this.iconThemeStore.onDidChange(themes => { iconThemeSettingSchema.enum = [null, ...themes.map(t => t.settingsId)]; - iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions[0], ...themes.map(t => themeData.description || '')]; + iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...themes.map(t => t.description || '')]; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); + + if (this.currentIconTheme.isLoaded) { + this.iconThemeStore.findThemeData(this.currentIconTheme.id).then(theme => { + if (!theme) { + this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, undefined); + } else { + this.restoreFileIconTheme(); + } + }); + } }); } @@ -183,7 +198,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.currentColorTheme.setCustomColors(this.colorCustomizations); this.currentColorTheme.setCustomTokenColors(this.tokenColorCustomizations); this.updateDynamicCSSRules(this.currentColorTheme); - this.applyTheme(this.currentColorTheme, null, false); + this.applyTheme(this.currentColorTheme, undefined, false); } if (this.watchedIconThemeLocation && this.currentIconTheme && e.contains(this.watchedIconThemeLocation, FileChangeType.UPDATED)) { await this.currentIconTheme.reload(this.fileService); @@ -208,7 +223,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.onColorThemeChange.event; } - private initialize(): Promise<[IColorTheme, IFileIconTheme]> { + private initialize(): Promise<[IColorTheme | null, IFileIconTheme | null]> { let detectHCThemeSetting = this.configurationService.getValue(DETECT_HC_SETTING); let colorThemeSetting: string; @@ -218,14 +233,14 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); } - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING) || ''; + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); return Promise.all([ this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - return this.setColorTheme(theme && theme.id, null); + return this.setColorTheme(theme && theme.id, undefined); }), this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - return this.setFileIconTheme(theme && theme.id, null); + return this.setFileIconTheme(theme && theme.id, undefined); }), ]); } @@ -235,18 +250,18 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (e.affectsConfiguration(COLOR_THEME_SETTING)) { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { - this.setColorTheme(theme.id, null); + this.setColorTheme(theme.id, undefined); } }); } } if (e.affectsConfiguration(ICON_THEME_SETTING)) { - let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING) || ''; + let iconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); if (iconThemeSetting !== this.currentIconTheme.settingsId) { this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - this.setFileIconTheme(theme && theme.id, null); + this.setFileIconTheme(theme && theme.id, undefined); }); } } @@ -280,7 +295,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.getColorTheme(); } - public setColorTheme(themeId: string, settingsTarget: ConfigurationTarget | null): Promise { + public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise { if (!themeId) { return Promise.resolve(null); } @@ -290,10 +305,11 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { themeId = validateThemeId(themeId); // migrate theme ids - return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(themeData => { - if (!themeData) { + return this.colorThemeStore.findThemeData(themeId, DEFAULT_THEME_ID).then(data => { + if (!data) { return null; } + const themeData = data; return themeData.ensureLoaded(this.fileService).then(_ => { if (themeId === this.currentColorTheme.id && !this.currentColorTheme.isLoaded && this.currentColorTheme.hasEqualData(themeData)) { // the loaded theme is identical to the perisisted theme. Don't need to send an event. @@ -307,7 +323,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.updateDynamicCSSRules(themeData); return this.applyTheme(themeData, settingsTarget); }, error => { - return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location.toString(), error.message))); + return Promise.reject(new Error(nls.localize('error.cannotloadtheme', "Unable to load {0}: {1}", themeData.location!.toString(), error.message))); }); }); } @@ -315,9 +331,9 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { public restoreColorTheme() { let colorThemeSetting = this.configurationService.getValue(COLOR_THEME_SETTING); if (colorThemeSetting !== this.currentColorTheme.settingsId) { - this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, null).then(theme => { + this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, undefined).then(theme => { if (theme) { - this.setColorTheme(theme.id, null); + this.setColorTheme(theme.id, undefined); } }); } @@ -338,7 +354,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _applyRules(cssRules.join('\n'), colorThemeRulesClassName); } - private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget, silent = false): Promise { + private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined, silent = false): Promise { if (this.container) { if (this.currentColorTheme) { removeClasses(this.container, this.currentColorTheme.id); @@ -355,7 +371,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) { if (this.watchedColorThemeLocation) { this.fileService.unwatchFileChanges(this.watchedColorThemeLocation); - this.watchedColorThemeLocation = null; + this.watchedColorThemeLocation = undefined; } if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { this.watchedColorThemeLocation = newTheme.location; @@ -377,7 +393,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeColorThemeConfiguration(settingsTarget); } - private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget): Promise { + private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); } @@ -385,7 +401,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } private themeExtensionsActivated = new Map(); - private sendTelemetry(themeId: string, themeData: ExtensionData, themeType: string) { + private sendTelemetry(themeId: string, themeData: ExtensionData | undefined, themeType: string) { if (themeData) { let key = themeType + themeData.extensionId; if (!this.themeExtensionsActivated.get(key)) { @@ -422,7 +438,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.currentIconTheme; } - public setFileIconTheme(iconTheme: string, settingsTarget: ConfigurationTarget): Promise { + public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise { iconTheme = iconTheme || ''; if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { return this.writeFileIconConfiguration(settingsTarget); @@ -436,16 +452,25 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeFileIconConfiguration(settingsTarget); }; - return this.iconThemeStore.findThemeData(iconTheme).then(iconThemeData => { - if (!iconThemeData) { - iconThemeData = FileIconThemeData.noIconTheme(); - } + return this.iconThemeStore.findThemeData(iconTheme).then(data => { + const iconThemeData = data || FileIconThemeData.noIconTheme(); return iconThemeData.ensureLoaded(this.fileService).then(_ => { return _applyIconTheme(iconThemeData, onApply); }); }); } + public restoreFileIconTheme() { + let fileIconThemeSetting = this.configurationService.getValue(ICON_THEME_SETTING); + if (fileIconThemeSetting !== this.currentIconTheme.settingsId) { + this.iconThemeStore.findThemeBySettingsId(fileIconThemeSetting).then(theme => { + if (theme) { + this.setFileIconTheme(theme.id, undefined); + } + }); + } + } + private doSetFileIconTheme(iconThemeData: FileIconThemeData): void { this.currentIconTheme = iconThemeData; @@ -460,7 +485,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) { if (this.watchedIconThemeLocation) { this.fileService.unwatchFileChanges(this.watchedIconThemeLocation); - this.watchedIconThemeLocation = null; + this.watchedIconThemeLocation = undefined; } if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) { this.watchedIconThemeLocation = iconThemeData.location; @@ -475,7 +500,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } - private writeFileIconConfiguration(settingsTarget: ConfigurationTarget): Promise { + private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); } @@ -504,7 +529,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } function _applyIconTheme(data: FileIconThemeData, onApply: (theme: FileIconThemeData) => Promise): Promise { - _applyRules(data.styleSheetContent, iconThemeRulesClassName); + _applyRules(data.styleSheetContent!, iconThemeRulesClassName); return onApply(data); } @@ -571,14 +596,10 @@ const iconThemeSettingSchema: IConfigurationPropertySchema = { const colorCustomizationsSchema: IConfigurationPropertySchema = { type: 'object', description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), - properties: {}, - additionalProperties: false, + allOf: [{ properties: colorThemeSchema.colorsSchema.properties }], default: {}, defaultSnippets: [{ body: { - 'statusBar.background': '#666666', - 'panel.background': '#555555', - 'sideBar.background': '#444444' } }] }; @@ -609,7 +630,7 @@ function tokenGroupSettings(description: string) { }; } -const customEditorColorConfigurationProperties = { +const tokenColorConfigurationProperties: IJSONSchemaMap = { comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), @@ -619,19 +640,18 @@ const customEditorColorConfigurationProperties = { variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), [CUSTOM_EDITOR_SCOPE_COLORS_SETTING]: colorThemeSchema.tokenColorsSchema(nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).')) }; -const customEditorColorSchema: IConfigurationPropertySchema = { +const tokenColorCustomizationSchema: IConfigurationPropertySchema = { description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), default: {}, - additionalProperties: false, - properties: {} + allOf: [{ properties: tokenColorConfigurationProperties }] }; -const customEditorColorConfiguration: IConfigurationNode = { +const tokenColorCustomizationConfiguration: IConfigurationNode = { id: 'editor', order: 7.2, type: 'object', properties: { - [CUSTOM_EDITOR_COLORS_SETTING]: customEditorColorSchema + [CUSTOM_EDITOR_COLORS_SETTING]: tokenColorCustomizationSchema } }; -configurationRegistry.registerConfiguration(customEditorColorConfiguration); +configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration); From 5d00c4b2caf036d094ff347737efe5dcb0d22a3c Mon Sep 17 00:00:00 2001 From: isidor Date: Tue, 22 Jan 2019 15:52:02 +0100 Subject: [PATCH 0476/1066] fixes #66791 --- src/vs/workbench/parts/debug/browser/debugActions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 9a744bd4658..2eb0c164f12 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -96,7 +96,9 @@ export class ConfigureAction extends AbstractDebugAction { } private updateClass(): void { - this.class = this.debugService.getConfigurationManager().selectedConfiguration.name ? 'debug-action configure' : 'debug-action configure notification'; + const configurationManager = this.debugService.getConfigurationManager(); + const configurationCount = configurationManager.getLaunches().map(l => l.getConfigurationNames().length).reduce((sum, current) => sum + current); + this.class = configurationCount > 0 ? 'debug-action configure' : 'debug-action configure notification'; } public run(event?: any): Promise { From efc4d938188e90852c5c25dd96dd9412288c4fc5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Jan 2019 12:59:20 +0100 Subject: [PATCH 0477/1066] make argv handling reusable --- src/vs/code/electron-main/main.ts | 2 +- src/vs/code/node/cli.ts | 8 +- src/vs/code/test/node/argv.test.ts | 64 ++-- src/vs/platform/environment/node/argv.ts | 362 ++++++++---------- .../platform/environment/node/argvHelper.ts | 60 +++ 5 files changed, 269 insertions(+), 227 deletions(-) create mode 100644 src/vs/platform/environment/node/argvHelper.ts diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index df2b83a177e..39f624e5de8 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -8,7 +8,7 @@ import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import product from 'vs/platform/node/product'; -import { parseMainProcessArgv } from 'vs/platform/environment/node/argv'; +import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/node/paths'; import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 23e859420ae..d98a850e585 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -5,7 +5,7 @@ import { spawn, ChildProcess } from 'child_process'; import { assign } from 'vs/base/common/objects'; -import { parseCLIProcessArgv, buildHelpMessage } from 'vs/platform/environment/node/argv'; +import { buildHelpMessage, buildVersionMessage } from 'vs/platform/environment/node/argv'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/node/product'; import pkg from 'vs/platform/node/package'; @@ -20,6 +20,7 @@ import { writeFileAndFlushSync } from 'vs/base/node/extfs'; import { isWindows } from 'vs/base/common/platform'; import { ProfilingSession, Target } from 'v8-inspect-profiler'; import { createWaitMarkerFile } from 'vs/code/node/wait'; +import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; function shouldSpawnCliProcess(argv: ParsedArgs): boolean { return !!argv['install-source'] @@ -44,12 +45,13 @@ export async function main(argv: string[]): Promise { // Help if (args.help) { - console.log(buildHelpMessage(product.nameLong, product.applicationName, pkg.version)); + const executable = `${product.applicationName}${os.platform() === 'win32' ? '.exe' : ''}`; + console.log(buildHelpMessage(product.nameLong, executable, pkg.version)); } // Version Info else if (args.version) { - console.log(`${pkg.version}\n${product.commit}\n${process.arch}`); + console.log(buildVersionMessage(pkg.version, product.commit)); } // Extensions Management diff --git a/src/vs/code/test/node/argv.test.ts b/src/vs/code/test/node/argv.test.ts index aafca11dbd9..2988c631ee7 100644 --- a/src/vs/code/test/node/argv.test.ts +++ b/src/vs/code/test/node/argv.test.ts @@ -3,37 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { formatOptions } from 'vs/platform/environment/node/argv'; +import { formatOptions, Option } from 'vs/platform/environment/node/argv'; suite('formatOptions', () => { + + function o(id: string, description: string): Option { + return { + id, description, type: 'string' + }; + } + test('Text should display small columns correctly', () => { - assert.equal(formatOptions({ 'foo': 'bar' }, 80), ' foo bar'); - assert.equal( - formatOptions({ - 'f': 'bar', - 'fo': 'ba', - 'foo': 'b' - }, 80), - ' f bar\n' + - ' fo ba\n' + - ' foo b'); + assert.deepEqual( + formatOptions([ + o('foo', 'bar') + ], 80), + [' --foo bar'] + ); + assert.deepEqual( + formatOptions([ + o('f', 'bar'), + o('fo', 'ba'), + o('foo', 'b') + ], 80), + [ + ' --f bar', + ' --fo ba', + ' --foo b' + ]); }); test('Text should wrap', () => { - assert.equal( - formatOptions({ - 'foo': ('bar ').repeat(9) - }, 40), - ' foo bar bar bar bar bar bar bar bar\n' + - ' bar'); + assert.deepEqual( + formatOptions([ + o('foo', ('bar ').repeat(9)) + ], 40), + [ + ' --foo bar bar bar bar bar bar bar bar', + ' bar' + ]); }); test('Text should revert to the condensed view when the terminal is too narrow', () => { - assert.equal( - formatOptions({ - 'foo': ('bar ').repeat(9) - }, 30), - ' foo\n' + - ' bar bar bar bar bar bar bar bar bar '); + assert.deepEqual( + formatOptions([ + o('foo', ('bar ').repeat(9)) + ], 30), + [ + ' --foo', + ' bar bar bar bar bar bar bar bar bar ' + ]); }); }); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 61302275e2c..e2286ba56a7 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -3,214 +3,166 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as os from 'os'; import * as minimist from 'minimist'; -import * as assert from 'assert'; -import { firstIndex } from 'vs/base/common/arrays'; +import * as os from 'os'; import { localize } from 'vs/nls'; -import { ParsedArgs } from '../common/environment'; -import { isWindows } from 'vs/base/common/platform'; -import product from 'vs/platform/node/product'; -import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files'; - -const options: minimist.Opts = { - string: [ - 'locale', - 'user-data-dir', - 'extensions-dir', - 'folder-uri', - 'file-uri', - 'remote', - 'extensionDevelopmentPath', - 'extensionTestsPath', - 'install-extension', - 'disable-extension', - 'uninstall-extension', - 'debugId', - 'debugPluginHost', - 'debugBrkPluginHost', - 'debugSearch', - 'debugBrkSearch', - 'enable-proposed-api', - 'export-default-configuration', - 'install-source', - 'upload-logs', - 'driver', - 'trace-category-filter', - 'trace-options', - '_' - ], - boolean: [ - 'help', - 'version', - 'wait', - 'diff', - 'add', - 'goto', - 'new-window', - 'unity-launch', - 'reuse-window', - 'open-url', - 'prof-startup', - 'prof-code-loading', - 'verbose', - 'logExtensionHostCommunication', - 'disable-extensions', - 'list-extensions', - 'show-versions', - 'nolazy', - 'issue', - 'skip-getting-started', - 'skip-release-notes', - 'sticky-quickopen', - 'disable-restore-windows', - 'disable-telemetry', - 'disable-updates', - 'disable-crash-reporter', - 'skip-add-to-recently-opened', - 'status', - 'file-write', - 'file-chmod', - 'driver-verbose', - 'trace' - ], - alias: { - add: 'a', - help: 'h', - version: 'v', - wait: 'w', - diff: 'd', - goto: 'g', - status: 's', - 'new-window': 'n', - 'reuse-window': 'r', - performance: 'p', - 'disable-extensions': 'disableExtensions', - 'extensions-dir': 'extensionHomePath', - 'debugPluginHost': 'inspect-extensions', - 'debugBrkPluginHost': 'inspect-brk-extensions', - 'debugSearch': 'inspect-search', - 'debugBrkSearch': 'inspect-brk-search', - } -}; - -function validate(args: ParsedArgs): ParsedArgs { - if (args.goto) { - args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`."))); - } - - if (args['max-memory']) { - assert(args['max-memory'] >= MIN_MAX_MEMORY_SIZE_MB, `The max-memory argument cannot be specified lower than ${MIN_MAX_MEMORY_SIZE_MB} MB.`); - } - - return args; -} - -function stripAppPath(argv: string[]): string[] | undefined { - const index = firstIndex(argv, a => !/^-/.test(a)); - - if (index > -1) { - return [...argv.slice(0, index), ...argv.slice(index + 1)]; - } - return undefined; -} +import { ParsedArgs } from 'vs/platform/environment/common/environment'; /** - * Use this to parse raw code process.argv such as: `Electron . --verbose --wait` + * This code is also used by standalone cli's. Avoid adding any other dependencies. */ -export function parseMainProcessArgv(processArgv: string[]): ParsedArgs { - let [, ...args] = processArgv; - // If dev, remove the first non-option argument: it's the app location - if (process.env['VSCODE_DEV']) { - args = stripAppPath(args) || []; +class HelpCategories { + o = localize('optionsUpperCase', "Options"); + e = localize('extensionsManagement', "Extensions Management"); + t = localize('troubleshooting', "Troubleshooting"); +} + +export interface Option { + id: string; + type: 'boolean' | 'string'; + alias?: string; + args?: string | string[]; + description?: string; + cat?: keyof HelpCategories; +} + +export const options: Option[] = [ + { id: 'diff', type: 'boolean', cat: 'o', alias: 'd', args: ['file', 'file'], description: localize('diff', "Compare two files with each other.") }, + { id: 'add', type: 'boolean', cat: 'o', alias: 'a', args: 'folder', description: localize('add', "Add folder(s) to the last active window.") }, + { id: 'goto', type: 'boolean', cat: 'o', alias: 'g', args: 'file:line[:character]', description: localize('goto', "Open a file at the path on the specified line and character position.") }, + { id: 'new-window', type: 'boolean', cat: 'o', alias: 'n', description: localize('newWindow', "Force to open a new window.") }, + { id: 'reuse-window', type: 'boolean', cat: 'o', alias: 'r', description: localize('reuseWindow', "Force to open a file or folder in an already opened window.") }, + { id: 'wait', type: 'boolean', cat: 'o', alias: 'w', description: localize('wait', "Wait for the files to be closed before returning.") }, + { id: 'locale', type: 'string', cat: 'o', args: 'locale', description: localize('locale', "The locale to use (e.g. en-US or zh-TW).") }, + { id: 'user-data-dir', type: 'string', cat: 'o', args: 'dir', description: localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.") }, + { id: 'version', type: 'boolean', cat: 'o', alias: 'v', description: localize('version', "Print version.") }, + { id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") }, + { id: 'folder-uri', type: 'string', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, + { id: 'file-uri', type: 'string', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, + + { id: 'extensions-dir', type: 'string', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, + { id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, + { id: 'show-versions', type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, + { id: 'install-extension', type: 'string', cat: 'e', args: 'extension-id', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") }, + { id: 'uninstall-extension', type: 'string', cat: 'e', args: 'extension-id', description: localize('uninstallExtension', "Uninstalls an extension.") }, + { id: 'enable-proposed-api', type: 'string', cat: 'e', args: 'extension-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, + + { id: 'verbose', type: 'boolean', cat: 't', description: localize('verbose', "Print verbose output (implies --wait).") }, + { id: 'log', type: 'string', cat: 't', args: 'level', description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'.") }, + { id: 'status', type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") }, + { id: 'prof-modules', type: 'boolean', alias: 'p', cat: 't', description: localize('prof-modules', "Capture performance markers while loading JS modules and print them with 'F1 > Developer: Startup Performance") }, + { id: 'prof-startup', type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") }, + { id: 'disable-extensions', type: 'boolean', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, + { id: 'disable-extension', type: 'string', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") }, + + { id: 'inspect-extensions', type: 'string', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, + { id: 'inspect-brk-search', type: 'string', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, + { id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") }, + { id: 'upload-logs', type: 'string', cat: 't', description: localize('uploadLogs', "Uploads logs from current session to a secure endpoint.") }, + { id: 'max-memory', type: 'boolean', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") }, + + { id: 'remote', type: 'string' }, + { id: 'extensionDevelopmentPath', type: 'string' }, + { id: 'extensionTestsPath', type: 'string' }, + { id: 'debugId', type: 'string' }, + { id: 'inspect-search', type: 'string' }, + { id: 'inspect-brk-extensions', type: 'string' }, + { id: 'export-default-configuration', type: 'string' }, + { id: 'install-source', type: 'string' }, + { id: 'driver', type: 'string' }, + { id: 'logExtensionHostCommunication', type: 'boolean' }, + { id: 'skip-getting-started', type: 'boolean' }, + { id: 'skip-release-notes', type: 'boolean' }, + { id: 'sticky-quickopen', type: 'boolean' }, + { id: 'disable-restore-windows', type: 'boolean' }, + { id: 'disable-telemetry', type: 'boolean' }, + { id: 'disable-updates', type: 'boolean' }, + { id: 'disable-crash-reporter', type: 'boolean' }, + { id: 'skip-add-to-recently-opened', type: 'boolean' }, + { id: 'unity-launch', type: 'boolean' }, + { id: 'open-url', type: 'boolean' }, + { id: 'nolazy', type: 'boolean' }, + { id: 'issue', type: 'boolean' }, + { id: 'file-write', type: 'boolean' }, + { id: 'file-chmod', type: 'boolean' }, + { id: 'driver-verbose', type: 'boolean' }, + { id: 'force', type: 'boolean' }, + { id: 'trace-category-filter', type: 'string' }, + { id: 'trace-options', type: 'string' }, + { id: 'prof-code-loading', type: 'boolean' }, + + { id: 'debugPluginHost', type: 'string', alias: 'inspect-extensions' }, + { id: 'debugBrkPluginHost', type: 'string', alias: 'inspect-brk-extensions' } +]; + +export function parseArgs(args: string[], isOptionSupported = (_: Option) => true): ParsedArgs { + const alias: { [key: string]: string } = {}; + const string: string[] = []; + const boolean: string[] = []; + for (let o of options) { + if (o.alias && isOptionSupported(o)) { + alias[o.id] = o.alias; + } + if (o.type === 'string') { + string.push(o.id); + } else if (o.type === 'boolean') { + boolean.push(o.id); + } } - - return validate(parseArgs(args)); -} - -/** - * Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait` - */ -export function parseCLIProcessArgv(processArgv: string[]): ParsedArgs { - let [, , ...args] = processArgv; - - if (process.env['VSCODE_DEV']) { - args = stripAppPath(args) || []; + // remote aliases to avoid confusion + const parsedArgs = minimist(args, { string, boolean, alias }) as ParsedArgs; + for (let o of options) { + if (o.alias) { + delete parsedArgs[o.alias]; + } } - - return validate(parseArgs(args)); + return parsedArgs; } -/** - * Use this to parse code arguments such as `--verbose --wait` - */ -export function parseArgs(args: string[]): ParsedArgs { - return minimist(args, options) as ParsedArgs; +function formatUsage(option: Option) { + let args = ''; + if (option.args) { + if (Array.isArray(option.args)) { + args = ` <${option.args.join('> <')}>`; + } else { + args = ` <${option.args}>`; + } + } + if (option.alias) { + return `-${option.alias} --${option.id}${args}`; + } + return `--${option.id}${args}`; } -const optionsHelp: { [name: string]: string; } = { - '-d, --diff ': localize('diff', "Compare two files with each other."), - '-a, --add ': localize('add', "Add folder(s) to the last active window."), - '-g, --goto ': localize('goto', "Open a file at the path on the specified line and character position."), - '-n, --new-window': localize('newWindow', "Force to open a new window."), - '-r, --reuse-window': localize('reuseWindow', "Force to open a file or folder in an already opened window."), - '-w, --wait': localize('wait', "Wait for the files to be closed before returning."), - '--locale ': localize('locale', "The locale to use (e.g. en-US or zh-TW)."), - '--user-data-dir ': localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code."), - '-v, --version': localize('version', "Print version."), - '-h, --help': localize('help', "Print usage.") -}; - -const extensionsHelp: { [name: string]: string; } = { - '--extensions-dir ': localize('extensionHomePath', "Set the root path for extensions."), - '--list-extensions': localize('listExtensions', "List the installed extensions."), - '--show-versions': localize('showVersions', "Show versions of installed extensions, when using --list-extension."), - '--uninstall-extension ( | )': localize('uninstallExtension', "Uninstalls an extension."), - '--install-extension ( | )': localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts."), - '--enable-proposed-api ()': localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") -}; - -const troubleshootingHelp: { [name: string]: string; } = { - '--verbose': localize('verbose', "Print verbose output (implies --wait)."), - '--log ': localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'."), - '-s, --status': localize('status', "Print process usage and diagnostics information."), - '--prof-startup': localize('prof-startup', "Run CPU profilers during startup."), - '--prof-modules': localize('prof-modules', "Capture performance markers while loading JS modules and print them with 'F1 > Developer: Startup Performance'"), - '--disable-extensions': localize('disableExtensions', "Disable all installed extensions."), - '--disable-extension ': localize('disableExtension', "Disable an extension."), - '--inspect-extensions': localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI."), - '--inspect-brk-extensions': localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI."), - '--disable-gpu': localize('disableGPU', "Disable GPU hardware acceleration."), - '--upload-logs': localize('uploadLogs', "Uploads logs from current session to a secure endpoint."), - '--max-memory': localize('maxMemory', "Max memory size for a window (in Mbytes).") -}; - -export function formatOptions(options: { [name: string]: string; }, columns: number): string { - let keys = Object.keys(options); - let argLength = Math.max.apply(null, keys.map(k => k.length)) + 2/*left padding*/ + 1/*right padding*/; +// exported only for testing +export function formatOptions(docOptions: Option[], columns: number): string[] { + let usageTexts = docOptions.map(formatUsage); + let argLength = Math.max.apply(null, usageTexts.map(k => k.length)) + 2/*left padding*/ + 1/*right padding*/; if (columns - argLength < 25) { // Use a condensed version on narrow terminals - return keys.reduce((r, key) => r.concat([` ${key}`, ` ${options[key]}`]), [] as string[]).join('\n'); + return docOptions.reduce((r, o, i) => r.concat([` ${usageTexts[i]}`, ` ${o.description}`]), []); } let descriptionColumns = columns - argLength - 1; - let result = ''; - keys.forEach(k => { - let wrappedDescription = wrapText(options[k], descriptionColumns); - let keyPadding = (' ').repeat(argLength - k.length - 2/*left padding*/); - if (result.length > 0) { - result += '\n'; - } - result += ' ' + k + keyPadding + wrappedDescription[0]; + let result = []; + docOptions.forEach((o, i) => { + let usage = usageTexts[i]; + let wrappedDescription = wrapText(o.description!, descriptionColumns); + let keyPadding = indent(argLength - usage.length - 2/*left padding*/); + result.push(' ' + usage + keyPadding + wrappedDescription[0]); for (let i = 1; i < wrappedDescription.length; i++) { - result += '\n' + (' ').repeat(argLength) + wrappedDescription[i]; + result.push(indent(argLength) + wrappedDescription[i]); } }); return result; } +function indent(count: number): string { + return (' ').repeat(count); +} + function wrapText(text: string, columns: number): string[] { - let lines: string[] = []; + let lines = []; while (text.length) { let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); let line = text.slice(0, index).trim(); @@ -220,24 +172,34 @@ function wrapText(text: string, columns: number): string[] { return lines; } -export function buildHelpMessage(fullName: string, name: string, version: string): string { - const columns = (process.stdout).isTTY ? (process.stdout).columns : 80; - const executable = `${name}${os.platform() === 'win32' ? '.exe' : ''}`; +export function buildHelpMessage(productName: string, executableName: string, version: string, isOptionSupported = (_: Option) => true): string { + const columns = (process.stdout).isTTY && (process.stdout).columns || 80; - return `${fullName} ${version} + let categories = new HelpCategories(); -${ localize('usage', "Usage")}: ${executable} [${localize('options', "options")}] [${localize('paths', 'paths')}...] + let help = [`${productName} ${version}`]; + help.push(''); + help.push(`${localize('usage', "Usage")}: ${executableName} [${localize('options', "options")}][${localize('paths', 'paths')}...]`); + help.push(''); + if (os.platform() === 'win32') { + help.push(localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", executableName)); + } else { + help.push(localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", executableName)); + } + help.push(''); + for (let key in categories) { + let categoryOptions = options.filter(o => !!o.description && o.cat === key && isOptionSupported(o)); + if (categoryOptions.length) { + help.push(categories[key]); + help.push(...formatOptions(categoryOptions, columns)); + help.push(''); + } + } + return help.join('\n'); +} -${ isWindows ? localize('stdinWindows', "To read output from another program, append '-' (e.g. 'echo Hello World | {0} -')", product.applicationName) : localize('stdinUnix', "To read from stdin, append '-' (e.g. 'ps aux | grep code | {0} -')", product.applicationName)} - -${ localize('optionsUpperCase', "Options")}: -${formatOptions(optionsHelp, columns)} - -${ localize('extensionsManagement', "Extensions Management")}: -${formatOptions(extensionsHelp, columns)} - -${ localize('troubleshooting', "Troubleshooting")}: -${formatOptions(troubleshootingHelp, columns)}`; +export function buildVersionMessage(version: string, commit: string) { + return `${version || 'Unknown version'}\n${commit || 'Unknown commit'}\n${process.arch}`; } /** diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts new file mode 100644 index 00000000000..a69fda7a371 --- /dev/null +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { firstIndex } from 'vs/base/common/arrays'; +import { localize } from 'vs/nls'; +import { ParsedArgs } from '../common/environment'; +import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/common/files'; +import { parseArgs } from 'vs/platform/environment/node/argv'; + + +function validate(args: ParsedArgs): ParsedArgs { + if (args.goto) { + args._.forEach(arg => assert(/^(\w:)?[^:]+(:\d*){0,2}$/.test(arg), localize('gotoValidation', "Arguments in `--goto` mode should be in the format of `FILE(:LINE(:CHARACTER))`."))); + } + + if (args['max-memory']) { + assert(args['max-memory'] >= MIN_MAX_MEMORY_SIZE_MB, `The max-memory argument cannot be specified lower than ${MIN_MAX_MEMORY_SIZE_MB} MB.`); + } + + return args; +} + +function stripAppPath(argv: string[]): string[] | undefined { + const index = firstIndex(argv, a => !/^-/.test(a)); + + if (index > -1) { + return [...argv.slice(0, index), ...argv.slice(index + 1)]; + } + return undefined; +} + +/** + * Use this to parse raw code process.argv such as: `Electron . --verbose --wait` + */ +export function parseMainProcessArgv(processArgv: string[]): ParsedArgs { + let [, ...args] = processArgv; + + // If dev, remove the first non-option argument: it's the app location + if (process.env['VSCODE_DEV']) { + args = stripAppPath(args) || []; + } + + return validate(parseArgs(args)); +} + +/** + * Use this to parse raw code CLI process.argv such as: `Electron cli.js . --verbose --wait` + */ +export function parseCLIProcessArgv(processArgv: string[]): ParsedArgs { + let [, , ...args] = processArgv; + + if (process.env['VSCODE_DEV']) { + args = stripAppPath(args) || []; + } + + return validate(parseArgs(args)); +} \ No newline at end of file From 032f640ba7e7cad93049eb316a6d6080f2a79b56 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Jan 2019 12:59:57 +0100 Subject: [PATCH 0478/1066] support files in windowOpen --- src/vs/workbench/api/node/apiCommands.ts | 2 +- .../parts/files/electron-browser/fileCommands.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index d45fa9d6195..0e228988d60 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -55,7 +55,7 @@ export class OpenFolderAPICommand { uri = correctedUri; } - return executor.executeCommand('_files.windowOpen', [uri], forceNewWindow); + return executor.executeCommand('_files.windowOpen', { folderURIs: [uri], forceNewWindow }); } } CommandsRegistry.registerCommand(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute)); diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index b45e20aed65..e6f4b1e4b9a 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -76,9 +76,18 @@ export const ResourceSelectedForCompareContext = new RawContextKey('res export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace"); -export const openWindowCommand = (accessor: ServicesAccessor, paths: Array, forceNewWindow: boolean) => { +export const openWindowCommand = (accessor: ServicesAccessor, input: Array | { fileURIs: URI[], folderURIs: URI[], forceNewWindow: boolean }, forceNewWindow: boolean) => { const windowService = accessor.get(IWindowService); - windowService.openWindow(paths.map(p => typeof p === 'string' ? URI.file(p) : p), { forceNewWindow }); + if (Array.isArray(input)) { + windowService.openWindow(input.map(p => typeof p === 'string' ? URI.file(p) : p), { forceNewWindow }); + } else if (input) { + if (Array.isArray(input.folderURIs) && input.folderURIs.length) { + windowService.openWindow(input.folderURIs, { forceNewWindow: input.forceNewWindow }); + } + if (Array.isArray(input.fileURIs) && input.fileURIs.length) { + windowService.openWindow(input.fileURIs, { forceNewWindow: input.forceNewWindow, forceOpenWorkspaceAsFile: true }); + } + } }; function save( From 998d665c39343776ca000cd5a8b8d3ab54942001 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 21 Jan 2019 13:00:47 +0100 Subject: [PATCH 0479/1066] cli server for internal terminal --- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- .../api/node/extHostTerminalService.ts | 87 ++++++++++++++++++- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index c2a9c874792..6f9e14ee77f 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -112,7 +112,7 @@ export function createApiFactory( const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures)); const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors)); const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands)); - const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService)); + const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService, extHostCommands)); const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands)); const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService)); const extHostSearch = rpcProtocol.set(ExtHostContext.ExtHostSearch, new ExtHostSearch(rpcProtocol, schemeTransformer, extHostLogService)); diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 95ec3d5874a..76a5c2e82ec 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -14,6 +14,10 @@ import { ILogService } from 'vs/platform/log/common/log'; import { EXT_HOST_CREATION_DELAY } from 'vs/workbench/parts/terminal/common/terminal'; import { TerminalProcess } from 'vs/workbench/parts/terminal/node/terminalProcess'; import { timeout } from 'vs/base/common/async'; +import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; +import * as http from 'http'; +import * as fs from 'fs'; +import { ExtHostCommands } from 'vs/workbench/api/node/extHostCommands'; const RENDERER_NO_PROCESS_ID = -1; @@ -242,6 +246,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { private _terminalProcesses: { [id: number]: TerminalProcess } = {}; private _terminalRenderers: ExtHostTerminalRenderer[] = []; private _getTerminalPromises: { [id: number]: Promise } = {}; + private _cliServer: CLIServer | undefined; public get activeTerminal(): ExtHostTerminal { return this._activeTerminal; } public get terminals(): ExtHostTerminal[] { return this._terminals; } @@ -256,7 +261,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { constructor( mainContext: IMainContext, private _extHostConfiguration: ExtHostConfiguration, - private _logService: ILogService + private _logService: ILogService, + private _commands: ExtHostCommands ) { this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService); } @@ -412,6 +418,11 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // config and adding keys that are needed to create the process terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables')); + if (!this._cliServer) { + this._cliServer = new CLIServer(this._commands); + } + env['VSCODE_IPC_HOOK_CLI'] = this._cliServer.ipcHandlePath; + // 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')); @@ -421,6 +432,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { this._terminalProcesses[id].onProcessExit((exitCode) => this._onProcessExit(id, exitCode)); } + public $acceptProcessInput(id: number, data: string): void { this._terminalProcesses[id].input(data); } @@ -449,6 +461,12 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // Send exit event to main side this._proxy.$sendProcessExit(id, exitCode); + + if (this._cliServer && !Object.keys(this._terminalProcesses).length) { + this._cliServer.dispose(); + this._cliServer = undefined; + } + } private _getTerminalByIdEventually(id: number, retries: number = 5): Promise { @@ -520,3 +538,70 @@ class ApiRequest { this._callback.apply(proxy, [id].concat(this._args)); } } + + +class CLIServer { + + private _server: http.Server; + private _ipcHandlePath: string | undefined; + + constructor(private _commands: ExtHostCommands) { + this._server = http.createServer((req, res) => this.onRequest(req, res)); + this.setup().catch(err => { + console.error(err); + return ''; + }); + } + + public get ipcHandlePath() { + return this._ipcHandlePath; + } + + private async setup(): Promise { + this._ipcHandlePath = generateRandomPipeName(); + + try { + this._server.listen(this.ipcHandlePath); + this._server.on('error', err => console.error(err)); + } catch (err) { + console.error('Could not start open from terminal server.'); + } + + return this.ipcHandlePath; + } + private toURIs(strs: string[]): URI[] { + const result: URI[] = []; + if (Array.isArray(strs)) { + for (const s of strs) { + try { + result.push(URI.parse(s)); + } catch (e) { + // ignore + } + } + } + return result; + } + + private onRequest(req: http.IncomingMessage, res: http.ServerResponse): void { + const chunks: string[] = []; + req.setEncoding('utf8'); + req.on('data', (d: string) => chunks.push(d)); + req.on('end', () => { + const { fileURIs, folderURIs, forceNewWindow } = JSON.parse(chunks.join('')); + if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) { + this._commands.executeCommand('_files.windowOpen', { folderURIs: this.toURIs(folderURIs), fileURIs: this.toURIs(fileURIs), forceNewWindow }); + } + res.writeHead(200); + res.end(); + }); + } + + dispose(): void { + this._server.close(); + + if (this._ipcHandlePath && process.platform !== 'win32' && fs.existsSync(this._ipcHandlePath)) { + fs.unlinkSync(this._ipcHandlePath); + } + } +} From 585099e139a03b1a5e13d023efee773a3e4f1bbd Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 22 Jan 2019 14:31:15 +0100 Subject: [PATCH 0480/1066] support add/diffMode in windowService.openWindow --- src/vs/code/electron-main/windows.ts | 4 ++-- src/vs/platform/windows/common/windows.ts | 13 +++++++++++-- .../windows/electron-browser/windowService.ts | 4 ++-- .../windows/electron-main/windowsService.ts | 8 +++++--- src/vs/workbench/api/node/extHostTerminalService.ts | 4 ++-- .../parts/files/electron-browser/fileCommands.ts | 6 +++--- 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 09cc9f0fab4..563bb5f2d8f 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -491,7 +491,7 @@ export class WindowsManager implements IWindowsMainService { // Remember in recent document list (unless this opens for extension development) // Also do not add paths when files are opened for diffing, only if opened individually - if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) { + if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.diffMode) { const recentlyOpenedWorkspaces: Array = []; const recentlyOpenedFiles: URI[] = []; @@ -830,7 +830,7 @@ export class WindowsManager implements IWindowsMainService { // folders should be added to the existing window. if (!openConfig.addMode && isCommandLineOrAPICall) { const foldersToOpen = windowsToOpen.filter(path => !!path.folderUri); - if (foldersToOpen.length > 1) { + if (foldersToOpen.length > 1 && foldersToOpen.every(f => f.folderUri.scheme === Schemas.file)) { const workspace = this.workspacesMainService.createWorkspaceSync(foldersToOpen.map(folder => ({ uri: folder.folderUri }))); // Add workspace and remove folders thereby diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index b68575d2b2c..2d1761c2446 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -151,7 +151,7 @@ export interface IWindowsService { toggleSharedProcess(): Promise; // Global methods - openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise; + openWindow(windowId: number, paths: URI[], options?: IOpenSettings): Promise; openNewWindow(options?: INewWindowOptions): Promise; showWindow(windowId: number): Promise; getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; @@ -178,6 +178,15 @@ export interface IMessageBoxResult { checkboxChecked?: boolean; } +export interface IOpenSettings { + forceNewWindow?: boolean; + forceReuseWindow?: boolean; + forceOpenWorkspaceAsFile?: boolean; + diffMode?: boolean; + addMode?: boolean; + args?: ParsedArgs; +} + export interface IWindowService { _serviceBrand: any; @@ -206,7 +215,7 @@ export interface IWindowService { getRecentlyOpened(): Promise; focusWindow(): Promise; closeWindow(): Promise; - openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise; + openWindow(paths: URI[], options?: IOpenSettings): Promise; isFocused(): Promise; setDocumentEdited(flag: boolean): Promise; isMaximized(): Promise; diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index 224facad2fc..60457efd10c 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IWindowConfiguration, IDevToolsOptions } from 'vs/platform/windows/common/windows'; +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'; @@ -101,7 +101,7 @@ export class WindowService extends Disposable implements IWindowService { return this.windowsService.saveAndEnterWorkspace(this.windowId, path); } - openWindow(paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise { + openWindow(paths: URI[], options?: IOpenSettings): Promise { return this.windowsService.openWindow(this.windowId, paths, options); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 1f5557062d1..8e3998a11e3 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -9,7 +9,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; import product from 'vs/platform/node/product'; -import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, INewWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, INewWindowOptions, IOpenSettings } from 'vs/platform/windows/common/windows'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { shell, crashReporter, app, Menu, clipboard } from 'electron'; import { Event } from 'vs/base/common/event'; @@ -286,7 +286,7 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable }); } - async openWindow(windowId: number, paths: URI[], options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise { + async openWindow(windowId: number, paths: URI[], options?: IOpenSettings): Promise { this.logService.trace('windowsService#openWindow'); if (!paths || !paths.length) { return undefined; @@ -299,7 +299,9 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable cli: options && options.args ? { ...this.environmentService.args, ...options.args } : this.environmentService.args, forceNewWindow: options && options.forceNewWindow, forceReuseWindow: options && options.forceReuseWindow, - forceOpenWorkspaceAsFile: options && options.forceOpenWorkspaceAsFile + forceOpenWorkspaceAsFile: options && options.forceOpenWorkspaceAsFile, + diffMode: options && options.diffMode, + addMode: options && options.addMode }); } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 76a5c2e82ec..c3b1d9a853e 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -588,9 +588,9 @@ class CLIServer { req.setEncoding('utf8'); req.on('data', (d: string) => chunks.push(d)); req.on('end', () => { - const { fileURIs, folderURIs, forceNewWindow } = JSON.parse(chunks.join('')); + const { fileURIs, folderURIs, forceNewWindow, diffMode, addMode, forceReuseWindow } = JSON.parse(chunks.join('')); if (folderURIs && folderURIs.length || fileURIs && fileURIs.length) { - this._commands.executeCommand('_files.windowOpen', { folderURIs: this.toURIs(folderURIs), fileURIs: this.toURIs(fileURIs), forceNewWindow }); + this._commands.executeCommand('_files.windowOpen', { folderURIs: this.toURIs(folderURIs), fileURIs: this.toURIs(fileURIs), forceNewWindow, diffMode, addMode, forceReuseWindow }); } res.writeHead(200); res.end(); diff --git a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts index e6f4b1e4b9a..2ecfe05bd08 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileCommands.ts @@ -76,16 +76,16 @@ export const ResourceSelectedForCompareContext = new RawContextKey('res export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace', "Remove Folder from Workspace"); -export const openWindowCommand = (accessor: ServicesAccessor, input: Array | { fileURIs: URI[], folderURIs: URI[], forceNewWindow: boolean }, forceNewWindow: boolean) => { +export const openWindowCommand = (accessor: ServicesAccessor, input: Array | { fileURIs: URI[], folderURIs: URI[], forceNewWindow: boolean, forceReuseWindow?: boolean, diffMode?: boolean, addMode?: boolean }, forceNewWindow: boolean) => { const windowService = accessor.get(IWindowService); if (Array.isArray(input)) { windowService.openWindow(input.map(p => typeof p === 'string' ? URI.file(p) : p), { forceNewWindow }); } else if (input) { if (Array.isArray(input.folderURIs) && input.folderURIs.length) { - windowService.openWindow(input.folderURIs, { forceNewWindow: input.forceNewWindow }); + windowService.openWindow(input.folderURIs, { forceNewWindow: input.forceNewWindow, diffMode: input.diffMode, addMode: input.addMode, forceReuseWindow: input.forceReuseWindow }); } if (Array.isArray(input.fileURIs) && input.fileURIs.length) { - windowService.openWindow(input.fileURIs, { forceNewWindow: input.forceNewWindow, forceOpenWorkspaceAsFile: true }); + windowService.openWindow(input.fileURIs, { forceNewWindow: input.forceNewWindow, forceOpenWorkspaceAsFile: true, diffMode: input.diffMode, addMode: input.addMode, forceReuseWindow: input.forceReuseWindow }); } } }; From ddd17d938ffcdc40158b845c9abc25dcc2978c7e Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 22 Jan 2019 15:56:08 +0100 Subject: [PATCH 0481/1066] argv: fix null checks --- src/vs/platform/environment/node/argv.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index e2286ba56a7..d7e62cbfd82 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -144,7 +144,7 @@ export function formatOptions(docOptions: Option[], columns: number): string[] { return docOptions.reduce((r, o, i) => r.concat([` ${usageTexts[i]}`, ` ${o.description}`]), []); } let descriptionColumns = columns - argLength - 1; - let result = []; + let result: string[] = []; docOptions.forEach((o, i) => { let usage = usageTexts[i]; let wrappedDescription = wrapText(o.description!, descriptionColumns); @@ -162,7 +162,7 @@ function indent(count: number): string { } function wrapText(text: string, columns: number): string[] { - let lines = []; + let lines: string[] = []; while (text.length) { let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); let line = text.slice(0, index).trim(); @@ -198,8 +198,8 @@ export function buildHelpMessage(productName: string, executableName: string, ve return help.join('\n'); } -export function buildVersionMessage(version: string, commit: string) { - return `${version || 'Unknown version'}\n${commit || 'Unknown commit'}\n${process.arch}`; +export function buildVersionMessage(version: string | undefined, commit: string | undefined): string { + return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`; } /** From 35689d126124d7b18f5dfb977ea8b3b411c704ef Mon Sep 17 00:00:00 2001 From: Yash Garg Date: Tue, 22 Jan 2019 20:38:28 +0530 Subject: [PATCH 0482/1066] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7294e71b9b..d5bebc76f27 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ VS Code is updated monthly with new features and bug fixes. You can download it VS Code in action

-The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways you can participate in the project, for example: +The [`vscode`](https://github.com/microsoft/vscode) repository is where VS Code is developed and there are many ways in which you can participate in the project, for example: * [Submit bugs and feature requests](https://github.com/microsoft/vscode/issues) and help us verify as they are checked in. * Review [source code changes](https://github.com/microsoft/vscode/pulls). From a865e6691bbcfa842ff95bd3f06578decc2becbc Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 22 Jan 2019 07:47:00 -0800 Subject: [PATCH 0483/1066] Sanitize terminal environment variables --- src/vs/workbench/api/node/extHostTerminalService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index c3b1d9a853e..90a3c7ad382 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -414,6 +414,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { // terminalEnvironment.mergeEnvironments(env, envFromConfig); terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); + // Sanitize the environment, removing any undesirable VS Code and Electron environment + // variables + terminalEnvironment.sanitizeEnvironment(env); + // Continue env initialization, merging in the env from the launch // config and adding keys that are needed to create the process terminalEnvironment.addTerminalEnvironmentKeys(env, platform.locale, terminalConfig.get('setLocaleVariables')); From c5486fe763ee3b05767d66a3a5331dbdfe93cacc Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 22 Jan 2019 19:06:06 +0100 Subject: [PATCH 0484/1066] #66907 Support non file workspaces - File workspaces: Use node services until file service is acquired - Non file workspaces: Use cached workspace until file service is acquired --- .../configuration/node/configuration.ts | 314 ++++++++++++++---- .../node/configurationService.ts | 16 +- .../configurationService.test.ts | 21 +- 3 files changed, 275 insertions(+), 76 deletions(-) diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 673cf4ed0da..b789593e4c9 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -15,11 +15,10 @@ import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { RunOnceScheduler, Delayer } from 'vs/base/common/async'; import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files'; import { isLinux } from 'vs/base/common/platform'; -import { ConfigWatcher } from 'vs/base/node/config'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; -import { IStoredWorkspace, IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import * as extfs from 'vs/base/node/extfs'; import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -32,94 +31,280 @@ import { IConfigurationModel } from 'vs/platform/configuration/common/configurat export class WorkspaceConfiguration extends Disposable { - private _workspaceConfigPath: URI; - private _workspaceConfigurationWatcher: ConfigWatcher; - private _workspaceConfigurationWatcherDisposables: IDisposable[] = []; + private readonly _cachedConfiguration: CachedWorkspaceConfiguration; + private _workspaceConfiguration: IWorkspaceConfiguration | null; + private _workspaceIdentifier: IWorkspaceIdentifier | null = null; + private _fileService: IFileService | null = null; private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); public readonly onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; - private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceConfigPath ? this._workspaceConfigPath.fsPath : ''); - private _cache: ConfigurationModel = new ConfigurationModel(); + constructor( + environmentService: IEnvironmentService + ) { + super(); + this._cachedConfiguration = new CachedWorkspaceConfiguration(environmentService); + this._workspaceConfiguration = this._cachedConfiguration; + } - load(workspaceConfigPath: URI): Promise { - if (this._workspaceConfigPath && this._workspaceConfigPath.fsPath === workspaceConfigPath.fsPath) { - return this.reload(); - } - - this._workspaceConfigPath = workspaceConfigPath; - - return new Promise((c, e) => { - const defaultConfig = new WorkspaceConfigurationModelParser(this._workspaceConfigPath.fsPath); - defaultConfig.parse(JSON.stringify({ folders: [] } as IStoredWorkspace, null, '\t')); - if (this._workspaceConfigurationWatcher) { - this.disposeConfigurationWatcher(); + adopt(fileService: IFileService): Promise { + if (!this._fileService) { + this._fileService = fileService; + if (this.adoptWorkspaceConfiguration()) { + if (this._workspaceIdentifier) { + return this._workspaceConfiguration.load(this._workspaceIdentifier).then(() => true); + } } - this._workspaceConfigurationWatcher = new ConfigWatcher(this._workspaceConfigPath.fsPath, { - changeBufferDelay: 300, - onError: error => errors.onUnexpectedError(error), - defaultConfig, - parse: (content: string, parseErrors: any[]) => { - this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this._workspaceConfigPath.fsPath); - this._workspaceConfigurationModelParser.parse(content); - parseErrors = [...this._workspaceConfigurationModelParser.errors]; - this.consolidate(); - return this._workspaceConfigurationModelParser; - }, initCallback: () => c(undefined) - }); - this.listenToWatcher(); - }); + } + return Promise.resolve(false); + } + + load(workspaceIdentifier: IWorkspaceIdentifier): Promise { + this._workspaceIdentifier = workspaceIdentifier; + this.adoptWorkspaceConfiguration(); + return this._workspaceConfiguration.load(this._workspaceIdentifier); } reload(): Promise { - this.stopListeningToWatcher(); - return new Promise(c => this._workspaceConfigurationWatcher.reload(() => { - this.listenToWatcher(); - c(undefined); - })); + return this._workspaceIdentifier ? this.load(this._workspaceIdentifier) : Promise.resolve(); + } + + getFolders(): IStoredWorkspaceFolder[] { + return this._workspaceConfiguration.getFolders(); + } + + setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise { + if (this._workspaceIdentifier) { + return jsonEditingService.write(URI.file(this._workspaceIdentifier.configPath), { key: 'folders', value: folders }, true) + .then(() => this.reload()); + } + return Promise.resolve(); + } + + getConfiguration(): ConfigurationModel { + return this._workspaceConfiguration.getWorkspaceSettings(); + } + + reprocessWorkspaceSettings(): ConfigurationModel { + this._workspaceConfiguration.reprocessWorkspaceSettings(); + return this.getConfiguration(); + } + + private adoptWorkspaceConfiguration(): boolean { + if (this._workspaceIdentifier) { + if (this._fileService) { + if (!(this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration)) { + dispose(this._workspaceConfiguration); + const nodeBasedWorkspaceConfiguration = this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration ? this._workspaceConfiguration : undefined; + this._workspaceConfiguration = new FileServiceBasedWorkspaceConfiguration(this._fileService, nodeBasedWorkspaceConfiguration); + this._register(this._workspaceConfiguration.onDidChange(e => this.onDidWorkspaceConfigurationChange())); + return !nodeBasedWorkspaceConfiguration; + } + return false; + } + if (URI.file(this._workspaceIdentifier.configPath).scheme === Schemas.file) { + if (!(this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration)) { + dispose(this._workspaceConfiguration); + this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration(); + return true; + } + return false; + } + } + return false; + } + + private onDidWorkspaceConfigurationChange(): void { + this.updateCache(); + this.reload().then(() => this._onDidUpdateConfiguration.fire()); + } + + private updateCache(): Promise { + if (this._workspaceIdentifier && URI.file(this._workspaceIdentifier.configPath).scheme !== Schemas.file && this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration) { + return this._workspaceConfiguration.load(this._workspaceIdentifier) + .then(() => this._cachedConfiguration.updateWorkspace(this._workspaceIdentifier, this._workspaceConfiguration.getConfigurationModel())); + } + return Promise.resolve(undefined); + } +} + +interface IWorkspaceConfiguration extends IDisposable { + readonly onDidChange: Event; + load(workspaceIdentifier: IWorkspaceIdentifier): Promise; + getConfigurationModel(): ConfigurationModel; + getFolders(): IStoredWorkspaceFolder[]; + getWorkspaceSettings(): ConfigurationModel; + reprocessWorkspaceSettings(): ConfigurationModel; +} + +abstract class AbstractWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration { + + private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser; + private _workspaceSettings: ConfigurationModel; + private _workspaceIdentifier: IWorkspaceIdentifier | null = null; + + protected readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + constructor(from?: AbstractWorkspaceConfiguration) { + super(); + + this._workspaceConfigurationModelParser = from ? from._workspaceConfigurationModelParser : new WorkspaceConfigurationModelParser(''); + this._workspaceSettings = new ConfigurationModel(); + } + + get workspaceIdentifier(): IWorkspaceIdentifier | null { + return this._workspaceIdentifier; + } + + load(workspaceIdentifier: IWorkspaceIdentifier): Promise { + this._workspaceIdentifier = workspaceIdentifier; + return this.loadWorkspaceConfigurationContents(workspaceIdentifier) + .then(contents => { + this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(workspaceIdentifier.id); + this._workspaceConfigurationModelParser.parse(contents); + this.consolidate(); + }); + } + + getConfigurationModel(): ConfigurationModel { + return this._workspaceConfigurationModelParser.configurationModel; } getFolders(): IStoredWorkspaceFolder[] { return this._workspaceConfigurationModelParser.folders; } - setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise { - return jsonEditingService.write(this._workspaceConfigPath, { key: 'folders', value: folders }, true) - .then(() => this.reload()); - } - - getConfiguration(): ConfigurationModel { - return this._cache; + getWorkspaceSettings(): ConfigurationModel { + return this._workspaceSettings; } reprocessWorkspaceSettings(): ConfigurationModel { this._workspaceConfigurationModelParser.reprocessWorkspaceSettings(); this.consolidate(); - return this.getConfiguration(); - } - - private listenToWatcher() { - this._workspaceConfigurationWatcher.onDidUpdateConfiguration(() => this._onDidUpdateConfiguration.fire(), this, this._workspaceConfigurationWatcherDisposables); - } - - private stopListeningToWatcher() { - this._workspaceConfigurationWatcherDisposables = dispose(this._workspaceConfigurationWatcherDisposables); + return this.getWorkspaceSettings(); } private consolidate(): void { - this._cache = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel); + this._workspaceSettings = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel); } - private disposeConfigurationWatcher(): void { - this.stopListeningToWatcher(); - if (this._workspaceConfigurationWatcher) { - this._workspaceConfigurationWatcher.dispose(); + protected abstract loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise; +} + +class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration { + + protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise { + return pfs.readFile(workspaceIdentifier.configPath) + .then(contents => contents.toString(), e => { + errors.onUnexpectedError(e); + return ''; + }); + } + +} + +class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration { + + private workspaceConfig: URI | null = null; + private readonly reloadConfigurationScheduler: RunOnceScheduler; + + constructor(private fileService: IFileService, from?: AbstractWorkspaceConfiguration) { + super(from); + this.workspaceConfig = from ? URI.file(from.workspaceIdentifier.configPath) : null; + this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); + this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); + } + + protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise { + this.workspaceConfig = URI.file(workspaceIdentifier.configPath); + return this.fileService.resolveContent(this.workspaceConfig) + .then(content => content.value, e => { + errors.onUnexpectedError(e); + return ''; + }); + } + + private handleWorkspaceFileEvents(event: FileChangesEvent): void { + if (this.workspaceConfig) { + const events = event.changes; + + let affectedByChanges = false; + // Find changes that affect workspace file + for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) { + affectedByChanges = resources.isEqual(this.workspaceConfig, events[i].resource); + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } + } + } +} + +class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration { + + private readonly _onDidChange: Emitter = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private cachedWorkspacePath: string; + private cachedConfigurationPath: string; + private _workspaceConfigurationModelParser: WorkspaceConfigurationModelParser; + private _workspaceSettings: ConfigurationModel; + + constructor(private environmentService: IEnvironmentService) { + super(); + this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(''); + this._workspaceSettings = new ConfigurationModel(); + } + + load(workspaceIdentifier: IWorkspaceIdentifier): Promise { + this.createPaths(workspaceIdentifier); + return pfs.readFile(this.cachedConfigurationPath) + .then(contents => { + this._workspaceConfigurationModelParser = new WorkspaceConfigurationModelParser(this.cachedConfigurationPath); + this._workspaceConfigurationModelParser.parse(contents.toString()); + this._workspaceSettings = this._workspaceConfigurationModelParser.settingsModel.merge(this._workspaceConfigurationModelParser.launchModel); + }, () => null); + } + + getConfigurationModel(): ConfigurationModel { + return this._workspaceConfigurationModelParser.configurationModel; + } + + getFolders(): IStoredWorkspaceFolder[] { + return this._workspaceConfigurationModelParser.folders; + } + + getWorkspaceSettings(): ConfigurationModel { + return this._workspaceSettings; + } + + reprocessWorkspaceSettings(): ConfigurationModel { + return this._workspaceSettings; + } + + async updateWorkspace(workspaceIdentifier: IWorkspaceIdentifier, configurationModel: ConfigurationModel): Promise { + try { + this.createPaths(workspaceIdentifier); + if (configurationModel.keys.length) { + const exists = await pfs.exists(this.cachedWorkspacePath); + if (!exists) { + await pfs.mkdirp(this.cachedWorkspacePath); + } + const raw = JSON.stringify(configurationModel.toJSON()); + await pfs.writeFile(this.cachedConfigurationPath, raw); + } else { + pfs.rimraf(this.cachedWorkspacePath); + } + } catch (error) { + errors.onUnexpectedError(error); } } - dispose(): void { - this.disposeConfigurationWatcher(); - super.dispose(); + private createPaths(workspaceIdentifier: IWorkspaceIdentifier) { + this.cachedWorkspacePath = paths.join(this.environmentService.userDataPath, 'CachedConfigurations', 'workspaces', workspaceIdentifier.id); + this.cachedConfigurationPath = paths.join(this.cachedWorkspacePath, 'workspace.json'); } } @@ -133,12 +318,11 @@ function isFolderSettingsConfigurationFile(resource: URI): boolean { return resources.isEqual(URI.from({ scheme: resource.scheme, path: resources.basename(resource) }), URI.from({ scheme: resource.scheme, path: `${FOLDER_SETTINGS_NAME}.json` })); } -export interface IFolderConfiguration { +export interface IFolderConfiguration extends IDisposable { readonly onDidChange: Event; readonly loaded: boolean; loadConfiguration(): Promise; reprocess(): ConfigurationModel; - dispose(): void; } export abstract class AbstractFolderConfiguration extends Disposable implements IFolderConfiguration { @@ -370,7 +554,7 @@ export class CachedFolderConfiguration extends Disposable implements IFolderConf configFolderRelativePath: string, environmentService: IEnvironmentService) { super(); - this.cachedFolderPath = paths.join(environmentService.userDataPath, 'CachedConfigurations', createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex')); + this.cachedFolderPath = paths.join(environmentService.userDataPath, 'CachedConfigurations', 'folders', createHash('md5').update(paths.join(folder.path, configFolderRelativePath)).digest('hex')); this.cachedConfigurationPath = paths.join(this.cachedFolderPath, 'configuration.json'); this.configurationModel = new ConfigurationModel(); } diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 41fbcfd2bc0..1ce7ae68da7 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -73,7 +73,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat this.defaultConfiguration = new DefaultConfigurationModel(); this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); - this.workspaceConfiguration = this._register(new WorkspaceConfiguration()); + this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService)); this._register(this.userConfiguration.onDidChangeConfiguration(userConfiguration => this.onUserConfigurationChanged(userConfiguration))); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); @@ -303,14 +303,18 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat acquireFileService(fileService: IFileService): void { this.fileService = fileService; const changedWorkspaceFolders: IWorkspaceFolder[] = []; - Promise.all(this.cachedFolderConfigs.values() + Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values() .map(folderConfiguration => folderConfiguration.adopt(fileService) .then(result => { if (result) { changedWorkspaceFolders.push(folderConfiguration.workspaceFolder); } - }))) - .then(() => { + return result; + }))]) + .then(([workspaceChanged]) => { + if (workspaceChanged) { + this.onWorkspaceConfigurationChanged(); + } for (const workspaceFolder of changedWorkspaceFolders) { this.onWorkspaceFolderConfigurationChanged(workspaceFolder); } @@ -335,9 +339,9 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat } private createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { - const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); - return this.workspaceConfiguration.load(workspaceConfigPath) + return this.workspaceConfiguration.load(workspaceIdentifier) .then(() => { + const workspaceConfigPath = URI.file(workspaceIdentifier.configPath); const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), URI.file(dirname(workspaceConfigPath.fsPath))); const workspaceId = workspaceIdentifier.id; return new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); diff --git a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts index ff7292b55e6..8d56534cfa1 100644 --- a/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/electron-browser/configurationService.test.ts @@ -19,7 +19,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { WorkspaceService } from 'vs/workbench/services/configuration/node/configurationService'; import { ISingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { ConfigurationEditingErrorCode } from 'vs/workbench/services/configuration/node/configurationEditingService'; -import { IFileService } from 'vs/platform/files/common/files'; +import { IFileService, FileChangesEvent, FileChangeType } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFoldersChangeEvent } from 'vs/platform/workspace/common/workspace'; import { ConfigurationTarget, IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { workbenchInstantiationService, TestTextResourceConfigurationService, TestTextFileService, TestLifecycleService, TestEnvironmentService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; @@ -34,6 +34,7 @@ import { JSONEditingService } from 'vs/workbench/services/configuration/node/jso import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; import { Uri } from 'vscode'; import { createHash } from 'crypto'; +import { Emitter, Event } from 'vs/base/common/event'; class SettingsTestEnvironmentService extends EnvironmentService { @@ -140,7 +141,7 @@ suite('WorkspaceContextService - Folder', () => { suite('WorkspaceContextService - Workspace', () => { - let parentResource: string, testObject: WorkspaceService; + let parentResource: string, testObject: WorkspaceService, instantiationService: TestInstantiationService, fileChangeEvent: Emitter = new Emitter(); setup(() => { return setUpWorkspace(['a', 'b']) @@ -151,14 +152,16 @@ suite('WorkspaceContextService - Workspace', () => { const environmentService = new SettingsTestEnvironmentService(parseArgs(process.argv), process.execPath, path.join(parentDir, 'settings.json')); const workspaceService = new WorkspaceService(environmentService); - const instantiationService = workbenchInstantiationService(); + instantiationService = workbenchInstantiationService(); instantiationService.stub(IWorkspaceContextService, workspaceService); instantiationService.stub(IConfigurationService, workspaceService); instantiationService.stub(IEnvironmentService, environmentService); return workspaceService.initialize({ id: configPath, configPath }).then(() => { - const fileService = new FileService(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), workspaceService, new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); + const fileService = new (class TestFileService extends FileService { + get onFileChanges(): Event { return fileChangeEvent.event; } + })(workspaceService, TestEnvironmentService, new TestTextResourceConfigurationService(), workspaceService, new TestLifecycleService(), new TestStorageService(), new TestNotificationService(), { disableWatcher: true }); instantiationService.stub(IFileService, fileService); instantiationService.stub(ITextFileService, instantiationService.createInstance(TestTextFileService)); instantiationService.stub(ITextModelService, instantiationService.createInstance(TextModelResolverService)); @@ -293,7 +296,15 @@ suite('WorkspaceContextService - Workspace', () => { done(); }); const workspace = { folders: [{ path: folders[0].uri.fsPath }, { path: folders[1].uri.fsPath }] }; - fs.writeFileSync(testObject.getWorkspace().configuration!.fsPath, JSON.stringify(workspace, null, '\t')); + instantiationService.get(IFileService).updateContent(testObject.getWorkspace().configuration, JSON.stringify(workspace, null, '\t')) + .then(() => { + fileChangeEvent.fire(new FileChangesEvent([ + { + resource: testObject.getWorkspace().configuration, + type: FileChangeType.UPDATED + } + ])); + }, done); }, done); }); From 7ed411c3c24d05aec016949eae9dab31762510a6 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 19:56:52 +0100 Subject: [PATCH 0485/1066] Improve error messages --- src/vs/workbench/api/node/extHostExtensionActivator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index dba9d4f2821..f5f6222ef99 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -256,7 +256,7 @@ export class ExtensionsActivator { if (!depDesc) { // Error condition 1: unknown dependency - this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' as the depending extension '{1}' is not found. Please install or enable the depending extension and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId)); + this._host.showMessage(Severity.Error, nls.localize('unknownDep', "Cannot activate extension '{0}' because it depends on extension '{1}', which is not installed or disabled. Please install or enable '{1}' and reload the window.", currentExtension.displayName || currentExtension.identifier.value, depId)); const error = new Error(`Unknown dependency '${depId}'`); this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); return; @@ -266,7 +266,7 @@ export class ExtensionsActivator { if (dep) { if (dep.activationFailed) { // Error condition 2: a dependency has already failed activation - this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' as the depending extension '{1}' is failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); + this._host.showMessage(Severity.Error, nls.localize('failedDep1', "Cannot activate extension '{0}' because it depends on extension '{1}', which failed to activate.", currentExtension.displayName || currentExtension.identifier.value, depId)); const error = new Error(`Dependency ${depId} failed to activate`); (error).detail = dep.activationFailedError; this._activatedExtensions.set(ExtensionIdentifier.toKey(currentExtension.identifier), new FailedExtension(error)); From c16d569a329e628538fc7c0de2f2d144196d4b47 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 20:01:56 +0100 Subject: [PATCH 0486/1066] Add IInitData.resolvedExtensions --- src/vs/workbench/api/node/extHost.protocol.ts | 1 + .../api/node/extHostExtensionActivator.ts | 15 ++++++++++++--- .../workbench/api/node/extHostExtensionService.ts | 2 +- .../extensions/electron-browser/extensionHost.ts | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index da87c79aaf5..7935656d6ba 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -66,6 +66,7 @@ export interface IInitData { parentPid: number; environment: IEnvironment; workspace: IWorkspaceData; + resolvedExtensions: ExtensionIdentifier[]; extensions: IExtensionDescription[]; telemetryInfo: ITelemetryInfo; logLevel: LogLevel; diff --git a/src/vs/workbench/api/node/extHostExtensionActivator.ts b/src/vs/workbench/api/node/extHostExtensionActivator.ts index f5f6222ef99..a21a307e7b9 100644 --- a/src/vs/workbench/api/node/extHostExtensionActivator.ts +++ b/src/vs/workbench/api/node/extHostExtensionActivator.ts @@ -191,6 +191,7 @@ export type ExtensionActivationReason = ExtensionActivatedByEvent | ExtensionAct export class ExtensionsActivator { private readonly _registry: ExtensionDescriptionRegistry; + private readonly _resolvedExtensionsSet: Set; private readonly _host: IExtensionsActivatorHost; private readonly _activatingExtensions: Map>; private readonly _activatedExtensions: Map; @@ -199,8 +200,10 @@ export class ExtensionsActivator { */ private readonly _alreadyActivatedEvents: { [activationEvent: string]: boolean; }; - constructor(registry: ExtensionDescriptionRegistry, host: IExtensionsActivatorHost) { + constructor(registry: ExtensionDescriptionRegistry, resolvedExtensions: ExtensionIdentifier[], host: IExtensionsActivatorHost) { this._registry = registry; + this._resolvedExtensionsSet = new Set(); + resolvedExtensions.forEach((extensionId) => this._resolvedExtensionsSet.add(ExtensionIdentifier.toKey(extensionId))); this._host = host; this._activatingExtensions = new Map>(); this._activatedExtensions = new Map(); @@ -251,8 +254,14 @@ export class ExtensionsActivator { let currentExtensionGetsGreenLight = true; for (let j = 0, lenJ = depIds.length; j < lenJ; j++) { - let depId = depIds[j]; - let depDesc = this._registry.getExtensionDescription(depId); + const depId = depIds[j]; + + if (this._resolvedExtensionsSet.has(ExtensionIdentifier.toKey(depId))) { + // This dependency is already resolved + continue; + } + + const depDesc = this._registry.getExtensionDescription(depId); if (!depDesc) { // Error condition 1: unknown dependency diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 60d464dc504..41dd9ad1fc9 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -192,7 +192,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { this._registry = new ExtensionDescriptionRegistry(initData.extensions); this._storage = new ExtHostStorage(this._extHostContext); this._storagePath = new ExtensionStoragePath(initData.workspace, initData.environment); - this._activator = new ExtensionsActivator(this._registry, { + this._activator = new ExtensionsActivator(this._registry, initData.resolvedExtensions, { showMessage: (severity: Severity, message: string): void => { this._mainThreadExtensionsProxy.$localShowMessage(severity, message); diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index a0548a72d60..5a6e51c6d3a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -430,6 +430,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { id: workspace.id, name: this._labelService.getWorkspaceLabel(workspace) }, + resolvedExtensions: [], extensions: extensionDescriptions, telemetryInfo, logLevel: this._logService.getLevel(), From b2736fdc7c0f2ea245c0d0374aab45f3a250a6cf Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 20:03:24 +0100 Subject: [PATCH 0487/1066] Capture console log and send it through the extension host protocol --- .../extensionHost.contribution.ts | 1 + .../api/electron-browser/mainThreadConsole.ts | 58 +++++++++++++++++++ src/vs/workbench/api/node/extHost.protocol.ts | 12 +++- src/vs/workbench/node/extensionHostMain.ts | 16 ++++- 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/vs/workbench/api/electron-browser/mainThreadConsole.ts diff --git a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts index 82a517e288c..e0e07ea27ce 100644 --- a/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/electron-browser/extensionHost.contribution.ts @@ -18,6 +18,7 @@ import 'vs/workbench/api/node/apiCommands'; import './mainThreadClipboard'; import './mainThreadCommands'; import './mainThreadConfiguration'; +import './mainThreadConsole'; import './mainThreadDebugService'; import './mainThreadDecorations'; import './mainThreadDiagnostics'; diff --git a/src/vs/workbench/api/electron-browser/mainThreadConsole.ts b/src/vs/workbench/api/electron-browser/mainThreadConsole.ts new file mode 100644 index 00000000000..0f4e37223aa --- /dev/null +++ b/src/vs/workbench/api/electron-browser/mainThreadConsole.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; +import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IRemoteConsoleLog, log, parse } from 'vs/base/node/console'; +import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/electron-browser/extensionHost'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IBroadcastService } from 'vs/platform/broadcast/electron-browser/broadcastService'; +import { EXTENSION_LOG_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost'; + +@extHostNamedCustomer(MainContext.MainThreadConsole) +export class MainThreadConsole implements MainThreadConsoleShape { + + private readonly _isExtensionDevHost: boolean; + private readonly _isExtensionDevTestFromCli: boolean; + + constructor( + extHostContext: IExtHostContext, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWindowsService private readonly _windowsService: IWindowsService, + @IBroadcastService private readonly _broadcastService: IBroadcastService, + ) { + const devOpts = parseExtensionDevOptions(this._environmentService); + this._isExtensionDevHost = devOpts.isExtensionDevHost; + this._isExtensionDevTestFromCli = devOpts.isExtensionDevTestFromCli; + } + + dispose(): void { + // + } + + $logExtensionHostMessage(entry: IRemoteConsoleLog): void { + // Send to local console unless we run tests from cli + if (!this._isExtensionDevTestFromCli) { + log(entry, 'Extension Host'); + } + + // Log on main side if running tests from cli + if (this._isExtensionDevTestFromCli) { + this._windowsService.log(entry.severity, ...parse(entry).args); + } + + // Broadcast to other windows if we are in development mode + else if (!this._environmentService.isBuilt || this._isExtensionDevHost) { + this._broadcastService.broadcast({ + channel: EXTENSION_LOG_BROADCAST_CHANNEL, + payload: { + logEntry: entry, + debugId: this._environmentService.debugExtensionHost.debugId + } + }); + } + } +} diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 7935656d6ba..d9f02bbeb03 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -37,13 +37,14 @@ import { IAdapterDescriptor, IConfig, ITerminalSettings } from 'vs/workbench/par import { ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; import { ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IRPCProtocol, ProxyIdentifier, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; +import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId } from 'vs/workbench/services/extensions/node/proxyIdentifier'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import * as vscode from 'vscode'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IRemoteConsoleLog } from 'vs/base/node/console'; export interface IEnvironment { isExtensionDevelopmentDebug: boolean; @@ -232,6 +233,10 @@ export interface MainThreadErrorsShape extends IDisposable { $onUnexpectedError(err: any | SerializedError): void; } +export interface MainThreadConsoleShape extends IDisposable { + $logExtensionHostMessage(msg: IRemoteConsoleLog): void; +} + export interface ISerializedRegExp { pattern: string; flags?: string; @@ -1068,10 +1073,11 @@ export interface ExtHostStorageShape { // --- proxy identifiers export const MainContext = { - MainThreadClipboard: >createMainId('MainThreadClipboard'), - MainThreadCommands: >createMainId('MainThreadCommands'), + MainThreadClipboard: createMainId('MainThreadClipboard'), + MainThreadCommands: createMainId('MainThreadCommands'), MainThreadComments: createMainId('MainThreadComments'), MainThreadConfiguration: createMainId('MainThreadConfiguration'), + MainThreadConsole: createMainId('MainThreadConsole'), MainThreadDebugService: createMainId('MainThreadDebugService'), MainThreadDecorations: createMainId('MainThreadDecorations'), MainThreadDiagnostics: createMainId('MainThreadDiagnostics'), diff --git a/src/vs/workbench/node/extensionHostMain.ts b/src/vs/workbench/node/extensionHostMain.ts index 06730f4e594..ec9cd8be9e4 100644 --- a/src/vs/workbench/node/extensionHostMain.ts +++ b/src/vs/workbench/node/extensionHostMain.ts @@ -10,7 +10,7 @@ import { Counter } from 'vs/base/common/numbers'; import { URI, setUriThrowOnMissingScheme } from 'vs/base/common/uri'; import { IURITransformer } from 'vs/base/common/uriIpc'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; -import { IEnvironment, IInitData, MainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { IEnvironment, IInitData, MainContext, MainThreadConsoleShape } from 'vs/workbench/api/node/extHost.protocol'; import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration'; import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionService'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; @@ -67,6 +67,8 @@ export class ExtensionHostMain { const allowExit = !!this._environment.extensionTestsPath; // to support other test frameworks like Jasmin that use process.exit (https://github.com/Microsoft/vscode/issues/37708) patchProcess(allowExit); + this._patchPatchedConsole(rpcProtocol.getProxy(MainContext.MainThreadConsole)); + // services this._extHostLogService = new ExtHostLogService(initData.logLevel, initData.logsLocation.fsPath); this.disposables.push(this._extHostLogService); @@ -114,6 +116,18 @@ export class ExtensionHostMain { }); } + private _patchPatchedConsole(mainThreadConsole: MainThreadConsoleShape): void { + // The console is already patched to use `process.send()` + const nativeProcessSend = process.send; + process.send = (...args: any[]) => { + if (args.length === 0 || !args[0] || args[0].type !== '__$console') { + return nativeProcessSend.apply(process, args); + } + + mainThreadConsole.$logExtensionHostMessage(args[0]); + }; + } + terminate(): void { if (this._isTerminating) { // we are already shutting down... From d1690ad706df71ca35b9212424bc112b4ba400f6 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 22 Jan 2019 11:25:41 -0800 Subject: [PATCH 0488/1066] 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 e68066faf00345e9ad5d6e20dfa3ba027b1c2903 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 20:35:59 +0100 Subject: [PATCH 0489/1066] Add possibility for error case --- .../remote/common/remoteAuthorityResolver.ts | 1 + .../remoteAuthorityResolverService.ts | 28 +++++++++---------- .../extensionHostProcessManager.ts | 11 ++++++++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/remote/common/remoteAuthorityResolver.ts b/src/vs/platform/remote/common/remoteAuthorityResolver.ts index d028e37cce8..9b87bb77a80 100644 --- a/src/vs/platform/remote/common/remoteAuthorityResolver.ts +++ b/src/vs/platform/remote/common/remoteAuthorityResolver.ts @@ -23,4 +23,5 @@ export interface IRemoteAuthorityResolverService { resolveAuthority(authority: string): Promise; setResolvedAuthority(resolvedAuthority: ResolvedAuthority): void; + setResolvedAuthorityError(authority: string, err: any): void; } diff --git a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts index 475677e05a2..ff3b4f673ab 100644 --- a/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/electron-browser/remoteAuthorityResolverService.ts @@ -19,37 +19,37 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS _serviceBrand: any; - private _pendingResolveAuthorityRequests: { [authority: string]: PendingResolveAuthorityRequest; }; - private _resolvedAuthorities: { [authority: string]: ResolvedAuthority; }; + private _resolveAuthorityRequests: { [authority: string]: PendingResolveAuthorityRequest; }; constructor() { - this._pendingResolveAuthorityRequests = Object.create(null); - this._resolvedAuthorities = Object.create(null); + this._resolveAuthorityRequests = Object.create(null); } resolveAuthority(authority: string): Promise { - if (this._resolvedAuthorities[authority]) { - return Promise.resolve(this._resolvedAuthorities[authority]); - } - if (!this._pendingResolveAuthorityRequests[authority]) { + if (!this._resolveAuthorityRequests[authority]) { let resolve: (value: ResolvedAuthority) => void; let reject: (err: any) => void; let promise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); - this._pendingResolveAuthorityRequests[authority] = new PendingResolveAuthorityRequest(resolve!, reject!, promise); + this._resolveAuthorityRequests[authority] = new PendingResolveAuthorityRequest(resolve!, reject!, promise); } - return this._pendingResolveAuthorityRequests[authority].promise; + return this._resolveAuthorityRequests[authority].promise; } setResolvedAuthority(resolvedAuthority: ResolvedAuthority) { - this._resolvedAuthorities[resolvedAuthority.authority] = resolvedAuthority; - if (this._pendingResolveAuthorityRequests[resolvedAuthority.authority]) { - let request = this._pendingResolveAuthorityRequests[resolvedAuthority.authority]; - delete this._pendingResolveAuthorityRequests[resolvedAuthority.authority]; + if (this._resolveAuthorityRequests[resolvedAuthority.authority]) { + let request = this._resolveAuthorityRequests[resolvedAuthority.authority]; ipc.send('vscode:remoteAuthorityResolved', resolvedAuthority); request.resolve(resolvedAuthority); } } + + setResolvedAuthorityError(authority: string, err: any): void { + if (this._resolveAuthorityRequests[authority]) { + let request = this._resolveAuthorityRequests[authority]; + request.reject(err); + } + } } diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts index 114d60030dc..fe57ae9ca52 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProcessManager.ts @@ -242,6 +242,17 @@ export class ExtensionHostProcessManager extends Disposable { } public resolveAuthority(remoteAuthority: string): Promise { + const authorityPlusIndex = remoteAuthority.indexOf('+'); + if (authorityPlusIndex === -1) { + // This authority does not need to be resolved, simply parse the port number + const pieces = remoteAuthority.split(':'); + return Promise.resolve({ + authority: remoteAuthority, + host: pieces[0], + port: parseInt(pieces[1], 10), + syncExtensions: false + }); + } return this._extensionHostProcessProxy.then(proxy => proxy.value.$resolveAuthority(remoteAuthority)); } From f6f255db3f64843bab066fbe7cc5aa4413f1aad8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 22:11:57 +0100 Subject: [PATCH 0490/1066] Simplify TextModel.tokenizeViewport (#66944) --- src/vs/editor/common/model/textModel.ts | 46 ++++++++++++------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index eb07a0cd49f..96acf1d7608 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1760,42 +1760,41 @@ export class TextModel extends Disposable implements model.ITextModel { public tokenizeViewport(startLineNumber: number, endLineNumber: number): void { if (!this._tokens.tokenizationSupport) { + // nothing to do return; } - // we tokenize `this._tokens.inValidLineStartIndex` lines in around 20ms so it's a good baseline. - const contextBefore = Math.floor(this._tokens.inValidLineStartIndex * 0.3); - startLineNumber = Math.max(1, startLineNumber - contextBefore); + startLineNumber = Math.max(1, startLineNumber); + endLineNumber = Math.min(this.getLineCount(), endLineNumber); + + if (endLineNumber <= this._tokens.inValidLineStartIndex) { + // nothing to do + return; + } if (startLineNumber <= this._tokens.inValidLineStartIndex) { + // tokenization has reached the viewport start... this.forceTokenization(endLineNumber); return; } - const eventBuilder = new ModelTokensChangedEventBuilder(); let nonWhitespaceColumn = this.getLineFirstNonWhitespaceColumn(startLineNumber); let fakeLines: string[] = []; - let i = startLineNumber - 1; let initialState: IState | null = null; - if (nonWhitespaceColumn > 0) { - while (nonWhitespaceColumn > 0 && i >= 1) { - let newNonWhitespaceIndex = this.getLineFirstNonWhitespaceColumn(i); + for (let i = startLineNumber - 1; nonWhitespaceColumn > 0 && i >= 1; i--) { + let newNonWhitespaceIndex = this.getLineFirstNonWhitespaceColumn(i); - if (newNonWhitespaceIndex === 0) { - i--; - continue; + if (newNonWhitespaceIndex === 0) { + continue; + } + + if (newNonWhitespaceIndex < nonWhitespaceColumn) { + initialState = this._tokens._getState(i - 1); + if (initialState) { + break; } - - if (newNonWhitespaceIndex < nonWhitespaceColumn) { - initialState = this._tokens._getState(i - 1); - if (initialState) { - break; - } - fakeLines.push(this.getLineContent(i)); - nonWhitespaceColumn = newNonWhitespaceIndex; - } - - i--; + fakeLines.push(this.getLineContent(i)); + nonWhitespaceColumn = newNonWhitespaceIndex; } } @@ -1813,8 +1812,7 @@ export class TextModel extends Disposable implements model.ITextModel { } } - const contextAfter = Math.floor(this._tokens.inValidLineStartIndex * 0.4); - endLineNumber = Math.min(this.getLineCount(), endLineNumber + contextAfter); + const eventBuilder = new ModelTokensChangedEventBuilder(); for (let i = startLineNumber; i <= endLineNumber; i++) { let text = this.getLineContent(i); let r = this._tokens._tokenizeText(this._buffer, text, state); From 042e96cb0878e6939c9279889cd293777d70722a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 22:12:46 +0100 Subject: [PATCH 0491/1066] Move tokenizeViewport call to the view model (#66944) --- src/vs/editor/browser/view/viewImpl.ts | 1 + src/vs/editor/browser/widget/codeEditorWidget.ts | 4 ---- src/vs/editor/common/viewModel/viewModel.ts | 1 + src/vs/editor/common/viewModel/viewModelImpl.ts | 7 +++++++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index fc88b66125f..f3b304c9142 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -445,6 +445,7 @@ export class View extends ViewEventHandler { public restoreState(scrollPosition: { scrollLeft: number; scrollTop: number; }): void { this._context.viewLayout.setScrollPositionNow({ scrollTop: scrollPosition.scrollTop }); + this._context.model.tokenizeViewport(); this._renderNow(); this.viewLines.updateLineWidths(); this._context.viewLayout.setScrollPositionNow({ scrollLeft: scrollPosition.scrollLeft }); diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index ae185d9d4e5..0b9ff7a4b37 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -842,10 +842,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } const reducedState = this._modelData.viewModel.reduceRestoreState(codeEditorState.viewState); - const linesViewportData = this._modelData.viewModel.viewLayout.getLinesViewportDataAtScrollTop(reducedState.scrollTop); - const startPosition = this._modelData.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(linesViewportData.startLineNumber, 1)); - const endPosition = this._modelData.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(linesViewportData.endLineNumber, 1)); - this._modelData.model.tokenizeViewport(startPosition.lineNumber, endPosition.lineNumber); this._modelData.view.restoreState(reducedState); } } diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index d147ce7ff08..3c3dffa41d5 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -119,6 +119,7 @@ export interface IViewModel { * Gives a hint that a lot of requests are about to come in for these line numbers. */ setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; + tokenizeViewport(): void; setHasFocus(hasFocus: boolean): void; getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[]; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 23bfb971b84..da7797e6e51 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -120,6 +120,13 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges); } + public tokenizeViewport(): void { + const linesViewportData = this.viewLayout.getLinesViewportData(); + const startPosition = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(linesViewportData.startLineNumber, 1)); + const endPosition = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(linesViewportData.endLineNumber, 1)); + this.model.tokenizeViewport(startPosition.lineNumber, endPosition.lineNumber); + } + public setHasFocus(hasFocus: boolean): void { this.hasFocus = hasFocus; } From 89721c99d7ee551e880952f6a979f97501170dc4 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 22 Jan 2019 22:37:02 +0100 Subject: [PATCH 0492/1066] Fixes #66944: Tokenize viewport with debouncing after scrolling and when the tokenization support has changed --- src/vs/editor/common/model/textModel.ts | 3 +++ src/vs/editor/common/model/textModelEvents.ts | 1 + src/vs/editor/common/model/textModelTokens.ts | 1 + src/vs/editor/common/viewModel/viewModelImpl.ts | 11 ++++++++++- src/vs/monaco.d.ts | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 96acf1d7608..44037d1b5b1 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -315,6 +315,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._resetTokenizationState(); this.emitModelTokensChangedEvent({ + tokenizationSupportChanged: true, ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() @@ -1841,6 +1842,7 @@ export class TextModel extends Disposable implements model.ITextModel { public flushTokens(): void { this._resetTokenizationState(); this.emitModelTokensChangedEvent({ + tokenizationSupportChanged: false, ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() @@ -1923,6 +1925,7 @@ export class TextModel extends Disposable implements model.ITextModel { this._resetTokenizationState(); this.emitModelTokensChangedEvent({ + tokenizationSupportChanged: true, ranges: [{ fromLineNumber: 1, toLineNumber: this.getLineCount() diff --git a/src/vs/editor/common/model/textModelEvents.ts b/src/vs/editor/common/model/textModelEvents.ts index 64f5d53e127..458566e1014 100644 --- a/src/vs/editor/common/model/textModelEvents.ts +++ b/src/vs/editor/common/model/textModelEvents.ts @@ -82,6 +82,7 @@ export interface IModelDecorationsChangedEvent { * An event describing that some ranges of lines have been tokenized (their tokens have changed). */ export interface IModelTokensChangedEvent { + readonly tokenizationSupportChanged: boolean; readonly ranges: { /** * The start of the range (inclusive) diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 02b3a946099..cff6814a9a7 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -500,6 +500,7 @@ export class ModelTokensChangedEventBuilder { return null; } return { + tokenizationSupportChanged: false, ranges: this._ranges }; } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index da7797e6e51..9681d2164b7 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -23,6 +23,7 @@ import { IViewModelLinesCollection, IdentityLinesCollection, SplitLinesCollectio import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLinesRenderingData, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations'; import { ITheme } from 'vs/platform/theme/common/themeService'; +import { RunOnceScheduler } from 'vs/base/common/async'; const USE_IDENTITY_LINES_COLLECTION = true; @@ -31,6 +32,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel private readonly editorId: number; private readonly configuration: editorCommon.IConfiguration; private readonly model: ITextModel; + private readonly _tokenizeViewportSoon: RunOnceScheduler; private hasFocus: boolean; private viewportStartLine: number; private viewportStartLineTrackedRange: string | null; @@ -38,7 +40,6 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel private readonly lines: IViewModelLinesCollection; public readonly coordinatesConverter: ICoordinatesConverter; public readonly viewLayout: ViewLayout; - private readonly decorations: ViewModelDecorations; constructor(editorId: number, configuration: editorCommon.IConfiguration, model: ITextModel, scheduleAtNextAnimationFrame: (callback: () => void) => IDisposable) { @@ -47,6 +48,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.editorId = editorId; this.configuration = configuration; this.model = model; + this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50)); this.hasFocus = false; this.viewportStartLine = -1; this.viewportStartLineTrackedRange = null; @@ -80,6 +82,9 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.viewLayout = this._register(new ViewLayout(this.configuration, this.getLineCount(), scheduleAtNextAnimationFrame)); this._register(this.viewLayout.onDidScroll((e) => { + if (e.scrollTopChanged) { + this._tokenizeViewportSoon.schedule(); + } try { const eventsCollector = this._beginEmit(); eventsCollector.emit(new viewEvents.ViewScrollChangedEvent(e)); @@ -280,6 +285,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } finally { this._endEmit(); } + + if (e.tokenizationSupportChanged) { + this._tokenizeViewportSoon.schedule(); + } })); this._register(this.model.onDidChangeLanguageConfiguration((e) => { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 22e332b8821..937ec711364 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2263,6 +2263,7 @@ declare namespace monaco.editor { * An event describing that some ranges of lines have been tokenized (their tokens have changed). */ export interface IModelTokensChangedEvent { + readonly tokenizationSupportChanged: boolean; readonly ranges: { /** * The start of the range (inclusive) From c800831ffcc8dce0e6625fdcc1c44b6ebf6c50c3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 14:21:53 -0800 Subject: [PATCH 0493/1066] Update js/ts grammar --- extensions/javascript/syntaxes/JavaScript.tmLanguage.json | 6 +++++- .../javascript/syntaxes/JavaScriptReact.tmLanguage.json | 6 +++++- extensions/typescript-basics/cgmanifest.json | 2 +- .../typescript-basics/syntaxes/TypeScript.tmLanguage.json | 6 +++++- .../syntaxes/TypeScriptReact.tmLanguage.json | 6 +++++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index bbe77ae2db9..869f8dafa44 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/f17fefd8d6bbf181ea27be2faccb2a274366e6db", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/76cfeabda164d2d132222af72e0a5f991e26c51a", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -3728,6 +3728,10 @@ { "include": "#type-function-return-type" }, + { + "name": "storage.modifier.js", + "match": "(? Date: Tue, 22 Jan 2019 14:22:36 -0800 Subject: [PATCH 0494/1066] Fix spelling --- .../src/features/completions.ts | 8 ++++---- .../typescript-language-features/src/features/hover.ts | 2 +- .../src/features/organizeImports.ts | 2 +- .../typescript-language-features/src/features/refactor.ts | 2 +- .../typescript-language-features/src/features/rename.ts | 2 +- .../src/features/signatureHelp.ts | 2 +- .../src/features/updatePathsOnRename.ts | 2 +- .../typescript-language-features/src/typescriptService.ts | 2 +- .../src/typescriptServiceClient.ts | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index a8a6040c5ae..f07e335daeb 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -369,7 +369,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider return null; } - await this.client.interuptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); + await this.client.interruptGetErr(() => this.fileConfigurationManager.ensureConfigurationForDocument(document, token)); const args: Proto.CompletionsRequestArgs = { ...typeConverters.Position.toFileLocationRequestArgs(file, position), @@ -385,7 +385,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider let entries: ReadonlyArray; let metadata: any | undefined; if (this.client.apiVersion.gte(API.v300)) { - const response = await this.client.interuptGetErr(() => this.client.execute('completionInfo', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('completionInfo', args, token)); if (response.type !== 'response' || !response.body) { return null; } @@ -403,7 +403,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider entries = response.body.entries; metadata = response.metadata; } else { - const response = await this.client.interuptGetErr(() => this.client.execute('completions', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('completions', args, token)); if (response.type !== 'response' || !response.body) { return null; } @@ -456,7 +456,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider ] }; - const response = await this.client.interuptGetErr(() => this.client.execute('completionEntryDetails', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('completionEntryDetails', args, token)); if (response.type !== 'response' || !response.body) { return item; } diff --git a/extensions/typescript-language-features/src/features/hover.ts b/extensions/typescript-language-features/src/features/hover.ts index fbd569cdb69..5077f9648a0 100644 --- a/extensions/typescript-language-features/src/features/hover.ts +++ b/extensions/typescript-language-features/src/features/hover.ts @@ -27,7 +27,7 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { } const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); - const response = await this.client.interuptGetErr(() => this.client.execute('quickinfo', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('quickinfo', args, token)); if (response.type !== 'response' || !response.body) { return undefined; } diff --git a/extensions/typescript-language-features/src/features/organizeImports.ts b/extensions/typescript-language-features/src/features/organizeImports.ts index 58dbea70714..aeb1efae11f 100644 --- a/extensions/typescript-language-features/src/features/organizeImports.ts +++ b/extensions/typescript-language-features/src/features/organizeImports.ts @@ -82,7 +82,7 @@ export class OrganizeImportsCodeActionProvider implements vscode.CodeActionProvi return []; } - if (!context.only || !vscode.CodeActionKind.SourceOrganizeImports.intersects(context.only)) { + if (!context.only || !context.only.contains(vscode.CodeActionKind.SourceOrganizeImports)) { return []; } diff --git a/extensions/typescript-language-features/src/features/refactor.ts b/extensions/typescript-language-features/src/features/refactor.ts index 5dfdc3de2e2..5f28e8e398b 100644 --- a/extensions/typescript-language-features/src/features/refactor.ts +++ b/extensions/typescript-language-features/src/features/refactor.ts @@ -138,7 +138,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { } const args: Proto.GetApplicableRefactorsRequestArgs = typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection); - const response = await this.client.interuptGetErr(() => { + const response = await this.client.interruptGetErr(() => { this.formattingOptionsManager.ensureConfigurationForDocument(document, token); return this.client.execute('getApplicableRefactors', args, token); diff --git a/extensions/typescript-language-features/src/features/rename.ts b/extensions/typescript-language-features/src/features/rename.ts index ebcc30a8c68..55e6c819a08 100644 --- a/extensions/typescript-language-features/src/features/rename.ts +++ b/extensions/typescript-language-features/src/features/rename.ts @@ -91,7 +91,7 @@ class TypeScriptRenameProvider implements vscode.RenameProvider { findInComments: false }; - return this.client.interuptGetErr(() => { + return this.client.interruptGetErr(() => { this.fileConfigurationManager.ensureConfigurationForDocument(document, token); return this.client.execute('rename', args, token); }); diff --git a/extensions/typescript-language-features/src/features/signatureHelp.ts b/extensions/typescript-language-features/src/features/signatureHelp.ts index bf1e380eb31..189185a37fc 100644 --- a/extensions/typescript-language-features/src/features/signatureHelp.ts +++ b/extensions/typescript-language-features/src/features/signatureHelp.ts @@ -33,7 +33,7 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), triggerReason: toTsTriggerReason(context) }; - const response = await this.client.interuptGetErr(() => this.client.execute('signatureHelp', args, token)); + const response = await this.client.interruptGetErr(() => this.client.execute('signatureHelp', args, token)); if (response.type !== 'response' || !response.body) { return undefined; } diff --git a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts index 4a0689efac5..355acbf90ea 100644 --- a/extensions/typescript-language-features/src/features/updatePathsOnRename.ts +++ b/extensions/typescript-language-features/src/features/updatePathsOnRename.ts @@ -233,7 +233,7 @@ class UpdateImportsOnFileRenameHandler extends Disposable { ): Promise { const isDirectoryRename = fs.lstatSync(newFile).isDirectory(); - const response = await this.client.interuptGetErr(() => { + const response = await this.client.interruptGetErr(() => { this.fileConfigurationManager.setGlobalConfigurationFromDocument(document, nulToken); const args: Proto.GetEditsForFileRenameRequestArgs & { file: string } = { file: targetResource, diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index fecd18e10d6..8f004c0e5eb 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -116,5 +116,5 @@ export interface ITypeScriptServiceClient { /** * Cancel on going geterr requests and re-queue them after `f` has been evaluated. */ - interuptGetErr(f: () => R): R; + interruptGetErr(f: () => R): R; } \ No newline at end of file diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index bc0a58ac97c..799f24864c1 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -641,7 +641,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType return runningServerState.server.executeImpl(command, args, executeInfo); } - public interuptGetErr(f: () => R): R { + public interruptGetErr(f: () => R): R { return this.bufferSyncSupport.interuptGetErr(f); } From 508f43166bd8551a26d3b53b2e04008d0b9c357c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 14:31:04 -0800 Subject: [PATCH 0495/1066] Rename source.autoFix to source.fixAll Part of #62110 `autoFix` is a confusing term since we have a `auto fix` command now. Using `fix all` as this term is used by many linters for this type of operation --- .../src/features/{autoFix.ts => fixAll.ts} | 6 +++--- .../typescript-language-features/src/languageProvider.ts | 2 +- src/vs/editor/common/config/commonEditorConfig.ts | 4 ++-- src/vs/vscode.proposed.d.ts | 7 +++++-- src/vs/workbench/api/node/extHostTypes.ts | 2 +- 5 files changed, 12 insertions(+), 9 deletions(-) rename extensions/typescript-language-features/src/features/{autoFix.ts => fixAll.ts} (95%) diff --git a/extensions/typescript-language-features/src/features/autoFix.ts b/extensions/typescript-language-features/src/features/fixAll.ts similarity index 95% rename from extensions/typescript-language-features/src/features/autoFix.ts rename to extensions/typescript-language-features/src/features/fixAll.ts index 2a9e9d1f0e7..8c6f09659c3 100644 --- a/extensions/typescript-language-features/src/features/autoFix.ts +++ b/extensions/typescript-language-features/src/features/fixAll.ts @@ -23,7 +23,7 @@ const autoFixableDiagnosticCodes = new Set([ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { public static readonly metadata: vscode.CodeActionProviderMetadata = { - providedCodeActionKinds: [vscode.CodeActionKind.SourceAutoFix] + providedCodeActionKinds: [vscode.CodeActionKind.SourceFixAll] }; constructor( @@ -38,7 +38,7 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { context: vscode.CodeActionContext, token: vscode.CancellationToken ): Promise { - if (!context.only || !vscode.CodeActionKind.SourceAutoFix.intersects(context.only)) { + if (!context.only || !vscode.CodeActionKind.SourceFixAll.intersects(context.only)) { return undefined; } @@ -82,7 +82,7 @@ class TypeScriptAutoFixProvider implements vscode.CodeActionProvider { const { edit, fixedDiagnostics } = autoFixResponse; const codeAction = new vscode.CodeAction( localize('autoFix.label', 'Auto fix'), - vscode.CodeActionKind.SourceAutoFix); + vscode.CodeActionKind.SourceFixAll); codeAction.edit = edit; codeAction.diagnostics = fixedDiagnostics; diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index beaa7b3b9f1..3a927cf777d 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -68,7 +68,7 @@ export default class LanguageProvider extends Disposable { this._register((await import('./features/jsDocCompletions')).register(selector, 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/autoFix')).register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager)); + this._register((await import('./features/fixAll')).register(selector, this.client, this.fileConfigurationManager, this.client.diagnosticsManager)); this._register((await import('./features/refactor')).register(selector, this.client, this.fileConfigurationManager, this.commandManager, this.telemetryReporter)); this._register((await import('./features/references')).register(selector, this.client)); this._register((await import('./features/referencesCodeLens')).register(selector, this.description.id, this.client, cachedResponse)); diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 63b2ee793a8..b88b781dbb7 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -839,9 +839,9 @@ const editorConfiguration: IConfigurationNode = { 'type': 'boolean', 'description': nls.localize('codeActionsOnSave.organizeImports', "Controls whether organize imports action should be run on file save.") }, - 'source.autoFix': { + 'source.fixAll': { 'type': 'boolean', - 'description': nls.localize('codeActionsOnSave.autoFix', "Controls whether auto fix action should be run on file save.") + 'description': nls.localize('codeActionsOnSave.fixAll', "Controls whether auto fix action should be run on file save.") } }, 'additionalProperties': { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 7da978be049..9b492047f9f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1134,9 +1134,12 @@ declare module 'vscode' { //#region Autofix - mjbvz export namespace CodeActionKind { /** - * Base kind for an auto fix source action: `source.autoFix`. + * Base kind for an auto fix source action: `source.fixAll`. + * + * Fix all actions automatically fix errors in the code that have a clear fix that does not require user input. + * They should not suppress errors or perform unsafe fixes such as generating new types or classes. */ - export const SourceAutoFix: CodeActionKind; + export const SourceFixAll: CodeActionKind; } //#endregion } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index 803950badb5..950d8cee119 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1022,7 +1022,7 @@ export class CodeActionKind { public static readonly RefactorRewrite = CodeActionKind.Refactor.append('rewrite'); public static readonly Source = CodeActionKind.Empty.append('source'); public static readonly SourceOrganizeImports = CodeActionKind.Source.append('organizeImports'); - public static readonly SourceAutoFix = CodeActionKind.Source.append('autoFix'); + public static readonly SourceFixAll = CodeActionKind.Source.append('fixAll'); constructor( public readonly value: string From cf27b0ed051394b0160f5183db5811ac21601126 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 15:04:53 -0800 Subject: [PATCH 0496/1066] Make sure we cancel code action requests for mainThreadSave If a code action provider takes too long to compute a code action during save, we skip that code action. However we do not cancel the request itself With this change, we now cancel `getCodeActions` when the request times out --- .../editor/contrib/codeAction/codeAction.ts | 10 ++++--- .../codeAction/test/codeAction.test.ts | 17 ++++++------ .../mainThreadSaveParticipant.ts | 26 +++++++++++++------ .../parts/markers/electron-browser/markers.ts | 4 +-- .../api/extHostLanguageFeatures.test.ts | 8 +++--- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index d203fb26d8c..75ab9dce71b 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -18,8 +18,8 @@ import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActio export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, - trigger?: CodeActionTrigger, - token: CancellationToken = CancellationToken.None + trigger: CodeActionTrigger | undefined, + token: CancellationToken ): Promise { const codeActionContext: CodeActionContext = { only: trigger && trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined, @@ -114,5 +114,9 @@ registerLanguageCommand('_executeCodeActionProvider', function (accessor, args) throw illegalArgument(); } - return getCodeActions(model, model.validateRange(range), { type: 'manual', filter: { includeSourceActions: true } }); + return getCodeActions( + model, + model.validateRange(range), + { type: 'manual', filter: { includeSourceActions: true } }, + CancellationToken.None); }); diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index 9a19c355fc5..ccd1a7dc0ed 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -11,6 +11,7 @@ import { CodeAction, CodeActionContext, CodeActionProvider, CodeActionProviderRe import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; +import { CancellationToken } from 'vs/base/common/cancellation'; suite('CodeAction', () => { @@ -115,7 +116,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const actions = await getCodeActions(model, new Range(1, 1, 2, 1)); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), undefined, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); @@ -134,20 +135,20 @@ suite('CodeAction', () => { disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); { - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 2); assert.strictEqual(actions[0].title, 'a'); assert.strictEqual(actions[1].title, 'a.b'); } { - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b') } }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a.b'); } { - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b.c') } }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b.c') } }, CancellationToken.None); assert.equal(actions.length, 0); } }); @@ -163,7 +164,7 @@ suite('CodeAction', () => { disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); }); @@ -181,13 +182,13 @@ suite('CodeAction', () => { disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); { - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'b'); } { - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: CodeActionKind.Source, includeSourceActions: true } }); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: CodeActionKind.Source, includeSourceActions: true } }, CancellationToken.None); assert.equal(actions.length, 1); assert.strictEqual(actions[0].title, 'a'); } @@ -211,7 +212,7 @@ suite('CodeAction', () => { filter: { kind: CodeActionKind.QuickFix } - }); + }, CancellationToken.None); assert.strictEqual(actions.length, 0); assert.strictEqual(wasInvoked, false); }); diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 3e233c987cf..783a76ab020 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -5,7 +5,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IdleValue, sequence } from 'vs/base/common/async'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import * as strings from 'vs/base/common/strings'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService'; @@ -316,18 +316,28 @@ class CodeActionOnParticipant implements ISaveParticipant { return undefined; } + const tokenSource = new CancellationTokenSource(); + const timeout = this._configurationService.getValue('editor.codeActionsOnSaveTimeout', settingsOverrides); return Promise.race([ new Promise((_resolve, reject) => - setTimeout(() => reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)), timeout)), - this.applyOnSaveActions(model, codeActionsOnSave) - ]).then(() => undefined); + setTimeout(() => { + tokenSource.cancel(); + reject(localize('codeActionsOnSave.didTimeout', "Aborted codeActionsOnSave after {0}ms", timeout)); + }, timeout)), + this.applyOnSaveActions(model, codeActionsOnSave, tokenSource.token) + ]).then(() => { + tokenSource.cancel(); + }, (e) => { + tokenSource.cancel(); + return Promise.reject(e); + }); } - private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: CodeActionKind[]): Promise { + private async applyOnSaveActions(model: ITextModel, codeActionsOnSave: CodeActionKind[], token: CancellationToken): Promise { for (const codeActionKind of codeActionsOnSave) { - const actionsToRun = await this.getActionsToRun(model, codeActionKind); + const actionsToRun = await this.getActionsToRun(model, codeActionKind, token); try { await this.applyCodeActions(actionsToRun); } catch { @@ -342,11 +352,11 @@ class CodeActionOnParticipant implements ISaveParticipant { } } - private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind) { + private getActionsToRun(model: ITextModel, codeActionKind: CodeActionKind, token: CancellationToken) { return getCodeActions(model, model.getFullModelRange(), { type: 'auto', filter: { kind: codeActionKind, includeSourceActions: true }, - }); + }, token); } } diff --git a/src/vs/workbench/parts/markers/electron-browser/markers.ts b/src/vs/workbench/parts/markers/electron-browser/markers.ts index 507c732f58f..6c8c060eefd 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markers.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markers.ts @@ -24,6 +24,7 @@ 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'; export const IMarkersWorkbenchService = createDecorator('markersWorkbenchService'); @@ -159,8 +160,7 @@ export class MarkersWorkbenchService extends Disposable implements IMarkersWorkb private async _getFixes(uri: URI, range?: Range): Promise { const model = this.modelService.getModel(uri); if (model) { - const codeActions = await getCodeActions(model, range ? range : model.getFullModelRange(), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }); - return codeActions; + return getCodeActions(model, range ? range : model.getFullModelRange(), { type: 'manual', filter: { kind: CodeActionKind.QuickFix } }, CancellationToken.None /* TODO: use cancellation here */); } return []; } diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 515d607340d..5b114c4ae8d 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -670,7 +670,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined).then(value => { + return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { assert.equal(value.length, 2); const [first, second] = value; @@ -697,7 +697,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined).then(value => { + return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { assert.equal(value.length, 1); const [first] = value; @@ -723,7 +723,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined).then(value => { + return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { assert.equal(value.length, 1); }); }); @@ -743,7 +743,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined).then(value => { + return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { assert.equal(value.length, 1); }); }); From 7b095ea78197e46014ecdf4e66dd5834fa82ff26 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 15:23:29 -0800 Subject: [PATCH 0497/1066] Pick up TS 3.3 rc #66175 --- extensions/package.json | 4 ++-- extensions/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index c162a8f58f5..d746f102903 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,9 +3,9 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.3.0-dev.20190108" + "typescript": "3.3.0-rc" }, "scripts": { "postinstall": "node ./postinstall" } -} +} \ No newline at end of file diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 7ba5f1f77f8..d9da95bb124 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.3.0-dev.20190108: - version "3.3.0-dev.20190108" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.0-dev.20190108.tgz#4ac2e3f7b619a961bfae0750412bf9e79fafe720" - integrity sha512-GNGczOoEyZtqbY72G6fNnyCsgSw/gSLyVxHfduDalKl0tOrIt+mEV5JyVQzEMsmcyO821/3QsCjulJHS5KFOWg== +typescript@3.3.0-rc: + version "3.3.0-rc" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.0-rc.tgz#700578b55868f99c79870c5611570663b3e21e44" + integrity sha512-C8FMc5pU+gFLm/rXKcwjHIeQnXJXfPNmONhKDSUWeVhyrnSqEI84EzoZylTqN0g6sJwdsn1W1AuG0RQRhfhSEA== From 8c75f078ff1c733b31c88f3bcd46b577aa9dcbb7 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 15:29:08 -0800 Subject: [PATCH 0498/1066] Always require getCodeActions.trigger --- src/tsconfig.strictNullChecks.json | 20 ++++++------------- .../editor/contrib/codeAction/codeAction.ts | 12 +++++------ .../codeAction/test/codeAction.test.ts | 2 +- .../api/extHostLanguageFeatures.test.ts | 8 ++++---- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 52c7d2073bd..b28472f8120 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -6,18 +6,19 @@ }, "include": [ "./typings", + "./vs/base/browser/**/*.ts", "./vs/base/common/**/*.ts", "./vs/base/node/**/*.ts", - "./vs/base/browser/**/*.ts", - "./vs/editor/common/**/*.ts", "./vs/editor/browser/**/*.ts", - "./vs/editor/test/**/*.ts", - "./vs/editor/contrib/smartSelect/**/*.ts", + "./vs/editor/common/**/*.ts", + "./vs/editor/contrib/codeAction/**/*.ts", "./vs/editor/contrib/format/**/*.ts", "./vs/editor/contrib/gotoError/**/*.ts", "./vs/editor/contrib/inPlaceReplace/**/*.ts", + "./vs/editor/contrib/smartSelect/**/*.ts", + "./vs/editor/contrib/snippet/**/*.ts", "./vs/editor/contrib/suggest/**/*.ts", - "./vs/editor/contrib/snippet/**/*.ts" + "./vs/editor/test/**/*.ts", ], "files": [ "./vs/base/parts/contextmenu/common/contextmenu.ts", @@ -156,15 +157,6 @@ "./vs/editor/contrib/caretOperations/test/moveCarretCommand.test.ts", "./vs/editor/contrib/caretOperations/transpose.ts", "./vs/editor/contrib/clipboard/clipboard.ts", - "./vs/editor/contrib/codeAction/codeAction.ts", - "./vs/editor/contrib/codeAction/codeActionCommands.ts", - "./vs/editor/contrib/codeAction/codeActionContributions.ts", - "./vs/editor/contrib/codeAction/codeActionModel.ts", - "./vs/editor/contrib/codeAction/codeActionTrigger.ts", - "./vs/editor/contrib/codeAction/codeActionWidget.ts", - "./vs/editor/contrib/codeAction/lightBulbWidget.ts", - "./vs/editor/contrib/codeAction/test/codeAction.test.ts", - "./vs/editor/contrib/codeAction/test/codeActionModel.test.ts", "./vs/editor/contrib/codelens/codelens.ts", "./vs/editor/contrib/codelens/codelensController.ts", "./vs/editor/contrib/codelens/codelensWidget.ts", diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 75ab9dce71b..b894b9dd3b8 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -18,12 +18,12 @@ import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActio export function getCodeActions( model: ITextModel, rangeOrSelection: Range | Selection, - trigger: CodeActionTrigger | undefined, + trigger: CodeActionTrigger, token: CancellationToken ): Promise { const codeActionContext: CodeActionContext = { - only: trigger && trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined, - trigger: trigger && trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic + only: trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined, + trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic }; const promises = CodeActionProviderRegistry.all(model) @@ -36,12 +36,12 @@ export function getCodeActions( return provider.providedCodeActionKinds.some(providedKind => { // Filter out actions by kind // The provided kind can be either a subset of a superset of the filtered kind - if (trigger && trigger.filter && trigger.filter.kind && !(trigger.filter.kind.contains(providedKind) || new CodeActionKind(providedKind).contains(trigger.filter.kind.value))) { + if (trigger.filter && trigger.filter.kind && !(trigger.filter.kind.contains(providedKind) || new CodeActionKind(providedKind).contains(trigger.filter.kind.value))) { return false; } // Don't return source actions unless they are explicitly requested - if (trigger && CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) { + if (CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) { return false; } @@ -53,7 +53,7 @@ export function getCodeActions( if (!Array.isArray(providedCodeActions)) { return []; } - return providedCodeActions.filter(action => isValidAction(trigger && trigger.filter, action)); + return providedCodeActions.filter(action => isValidAction(trigger.filter, action)); }, (err): CodeAction[] => { if (isPromiseCanceledError(err)) { throw err; diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index ccd1a7dc0ed..c19f65ceb69 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -116,7 +116,7 @@ suite('CodeAction', () => { testData.tsLint.abc ]; - const actions = await getCodeActions(model, new Range(1, 1, 2, 1), undefined, CancellationToken.None); + const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'manual' }, CancellationToken.None); assert.equal(actions.length, 6); assert.deepEqual(actions, expected); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 5b114c4ae8d..c320e581432 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -670,7 +670,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { + return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { assert.equal(value.length, 2); const [first, second] = value; @@ -697,7 +697,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { + return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { assert.equal(value.length, 1); const [first] = value; @@ -723,7 +723,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { + return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { assert.equal(value.length, 1); }); }); @@ -743,7 +743,7 @@ suite('ExtHostLanguageFeatures', function () { })); return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), undefined, CancellationToken.None).then(value => { + return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { assert.equal(value.length, 1); }); }); From 401dd1caf41cab09e866f054e112623245b73f30 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 15:32:37 -0800 Subject: [PATCH 0499/1066] Convert to test to use async --- .../api/extHostLanguageFeatures.test.ts | 743 +++++++----------- 1 file changed, 304 insertions(+), 439 deletions(-) diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index c320e581432..18053e12bf7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -132,7 +132,7 @@ suite('ExtHostLanguageFeatures', function () { // --- outline - test('DocumentSymbols, register/deregister', function () { + test('DocumentSymbols, register/deregister', async () => { assert.equal(modes.DocumentSymbolProviderRegistry.all(model).length, 0); let d1 = extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider { provideDocumentSymbols() { @@ -140,15 +140,14 @@ suite('ExtHostLanguageFeatures', function () { } }); - return rpcProtocol.sync().then(() => { - assert.equal(modes.DocumentSymbolProviderRegistry.all(model).length, 1); - d1.dispose(); - return rpcProtocol.sync(); - }); + await rpcProtocol.sync(); + assert.equal(modes.DocumentSymbolProviderRegistry.all(model).length, 1); + d1.dispose(); + return rpcProtocol.sync(); }); - test('DocumentSymbols, evil provider', function () { + test('DocumentSymbols, evil provider', async () => { disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider { provideDocumentSymbols(): any { throw new Error('evil document symbol provider'); @@ -160,36 +159,29 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDocumentSymbols(model, true, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getDocumentSymbols(model, true, CancellationToken.None); + assert.equal(value.length, 1); }); - test('DocumentSymbols, data conversion', function () { + test('DocumentSymbols, data conversion', async () => { disposables.push(extHost.registerDocumentSymbolProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentSymbolProvider { provideDocumentSymbols(): any { return [new types.SymbolInformation('test', types.SymbolKind.Field, new types.Range(0, 0, 0, 0))]; } })); - return rpcProtocol.sync().then(() => { - - return getDocumentSymbols(model, true, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - - let entry = value[0]; - assert.equal(entry.name, 'test'); - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - }); - }); + await rpcProtocol.sync(); + const value = await getDocumentSymbols(model, true, CancellationToken.None); + assert.equal(value.length, 1); + let entry = value[0]; + assert.equal(entry.name, 'test'); + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); // --- code lens - test('CodeLens, evil provider', function () { + test('CodeLens, evil provider', async () => { disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { provideCodeLenses(): any { @@ -202,14 +194,12 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getCodeLensData(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getCodeLensData(model, CancellationToken.None); + assert.equal(value.length, 1); }); - test('CodeLens, do not resolve a resolved lens', function () { + test('CodeLens, do not resolve a resolved lens', async () => { disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { provideCodeLenses(): any { @@ -222,20 +212,16 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getCodeLensData(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let data = value[0]; - return Promise.resolve(data.provider.resolveCodeLens(model, data.symbol, CancellationToken.None)).then(symbol => { - assert.equal(symbol.command.id, 'id'); - assert.equal(symbol.command.title, 'Title'); - }); - }); - }); + await rpcProtocol.sync(); + const value = await getCodeLensData(model, CancellationToken.None); + assert.equal(value.length, 1); + let data = value[0]; + const symbol = await Promise.resolve(data.provider.resolveCodeLens(model, data.symbol, CancellationToken.None)); + assert.equal(symbol.command.id, 'id'); + assert.equal(symbol.command.title, 'Title'); }); - test('CodeLens, missing command', function () { + test('CodeLens, missing command', async () => { disposables.push(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { provideCodeLenses() { @@ -243,24 +229,18 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getCodeLensData(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - - let data = value[0]; - return Promise.resolve(data.provider.resolveCodeLens(model, data.symbol, CancellationToken.None)).then(symbol => { - - assert.equal(symbol.command.id, 'missing'); - assert.equal(symbol.command.title, '!!MISSING: command!!'); - }); - }); - }); + await rpcProtocol.sync(); + const value = await getCodeLensData(model, CancellationToken.None); + assert.equal(value.length, 1); + let data = value[0]; + const symbol = await Promise.resolve(data.provider.resolveCodeLens(model, data.symbol, CancellationToken.None)); + assert.equal(symbol.command.id, 'missing'); + assert.equal(symbol.command.title, '!!MISSING: command!!'); }); // --- definition - test('Definition, data conversion', function () { + test('Definition, data conversion', async () => { disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider { provideDefinition(): any { @@ -268,18 +248,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); - assert.equal(entry.uri.toString(), model.uri.toString()); - }); - }); + await rpcProtocol.sync(); + let value = await getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); }); - test('Definition, one or many', function () { + test('Definition, one or many', async () => { disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider { provideDefinition(): any { @@ -292,15 +269,12 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 2); - }); - }); + await rpcProtocol.sync(); + const value = await getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 2); }); - test('Definition, registration order', function () { + test('Definition, registration order', async () => { disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider { provideDefinition(): any { @@ -314,19 +288,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 2); - // let [first, second] = value; - - assert.equal(value[0].uri.authority, 'second'); - assert.equal(value[1].uri.authority, 'first'); - }); - }); + await rpcProtocol.sync(); + const value = await getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 2); + // let [first, second] = value; + assert.equal(value[0].uri.authority, 'second'); + assert.equal(value[1].uri.authority, 'first'); }); - test('Definition, evil provider', function () { + test('Definition, evil provider', async () => { disposables.push(extHost.registerDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.DefinitionProvider { provideDefinition(): any { @@ -339,17 +309,14 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 1); }); // -- declaration - test('Declaration, data conversion', function () { + test('Declaration, data conversion', async () => { disposables.push(extHost.registerDeclarationProvider(defaultExtension, defaultSelector, new class implements vscode.DeclarationProvider { provideDeclaration(): any { @@ -357,20 +324,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getDeclarationsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); - assert.equal(entry.uri.toString(), model.uri.toString()); - }); - }); + await rpcProtocol.sync(); + let value = await getDeclarationsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); }); // --- implementation - test('Implementation, data conversion', function () { + test('Implementation, data conversion', async () => { disposables.push(extHost.registerImplementationProvider(defaultExtension, defaultSelector, new class implements vscode.ImplementationProvider { provideImplementation(): any { @@ -378,19 +342,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getImplementationsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); - assert.equal(entry.uri.toString(), model.uri.toString()); - }); - }); + await rpcProtocol.sync(); + let value = await getImplementationsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); }); // --- type definition - test('Type Definition, data conversion', function () { + test('Type Definition, data conversion', async () => { disposables.push(extHost.registerTypeDefinitionProvider(defaultExtension, defaultSelector, new class implements vscode.TypeDefinitionProvider { provideTypeDefinition(): any { @@ -398,19 +360,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getTypeDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); - assert.equal(entry.uri.toString(), model.uri.toString()); - }); - }); + await rpcProtocol.sync(); + let value = await getTypeDefinitionsAtPosition(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 2, startColumn: 3, endLineNumber: 4, endColumn: 5 }); + assert.equal(entry.uri.toString(), model.uri.toString()); }); // --- extra info - test('HoverProvider, word range at pos', function () { + test('HoverProvider, word range at pos', async () => { disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider { provideHover(): any { @@ -418,17 +378,16 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); - }); + await rpcProtocol.sync(); + getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); }); }); - test('HoverProvider, given range', function () { + test('HoverProvider, given range', async () => { disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider { provideHover(): any { @@ -436,18 +395,16 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 }); - }); + await rpcProtocol.sync(); + getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 4, startColumn: 1, endLineNumber: 9, endColumn: 8 }); }); }); - test('HoverProvider, registration order', function () { + test('HoverProvider, registration order', async () => { disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider { provideHover(): any { return new types.Hover('registered first'); @@ -461,18 +418,16 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - assert.equal(value.length, 2); - let [first, second] = value as modes.Hover[]; - assert.equal(first.contents[0].value, 'registered second'); - assert.equal(second.contents[0].value, 'registered first'); - }); - }); + await rpcProtocol.sync(); + const value = await getHover(model, new EditorPosition(1, 1), CancellationToken.None); + assert.equal(value.length, 2); + let [first, second] = (value as modes.Hover[]); + assert.equal(first.contents[0].value, 'registered second'); + assert.equal(second.contents[0].value, 'registered first'); }); - test('HoverProvider, evil provider', function () { + test('HoverProvider, evil provider', async () => { disposables.push(extHost.registerHoverProvider(defaultExtension, defaultSelector, new class implements vscode.HoverProvider { provideHover(): any { @@ -485,18 +440,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { - - assert.equal(value.length, 1); - }); + await rpcProtocol.sync(); + getHover(model, new EditorPosition(1, 1), CancellationToken.None).then(value => { + assert.equal(value.length, 1); }); }); // --- occurrences - test('Occurrences, data conversion', function () { + test('Occurrences, data conversion', async () => { disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { @@ -504,18 +456,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); - assert.equal(entry.kind, modes.DocumentHighlightKind.Text); - }); - }); + await rpcProtocol.sync(); + let value = await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); + assert.equal(entry.kind, modes.DocumentHighlightKind.Text); }); - test('Occurrences, order 1/2', function () { + test('Occurrences, order 1/2', async () => { disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { @@ -528,18 +477,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); - assert.equal(entry.kind, modes.DocumentHighlightKind.Text); - }); - }); + await rpcProtocol.sync(); + let value = await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 5 }); + assert.equal(entry.kind, modes.DocumentHighlightKind.Text); }); - test('Occurrences, order 2/2', function () { + test('Occurrences, order 2/2', async () => { disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { @@ -552,18 +498,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [entry] = value; - assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 }); - assert.equal(entry.kind, modes.DocumentHighlightKind.Text); - }); - }); + await rpcProtocol.sync(); + let value = await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); + let [entry] = value; + assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 }); + assert.equal(entry.kind, modes.DocumentHighlightKind.Text); }); - test('Occurrences, evil provider', function () { + test('Occurrences, evil provider', async () => { disposables.push(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { @@ -577,17 +520,14 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); }); // --- references - test('References, registration order', function () { + test('References, registration order', async () => { disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider { provideReferences(): any { @@ -601,19 +541,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return provideReferences(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 2); - - let [first, second] = value; - assert.equal(first.uri.path, '/second'); - assert.equal(second.uri.path, '/first'); - }); - }); + await rpcProtocol.sync(); + let value = await provideReferences(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 2); + let [first, second] = value; + assert.equal(first.uri.path, '/second'); + assert.equal(second.uri.path, '/first'); }); - test('References, data conversion', function () { + test('References, data conversion', async () => { disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider { provideReferences(): any { @@ -621,20 +557,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return provideReferences(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - - let [item] = value; - assert.deepEqual(item.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - assert.equal(item.uri.toString(), model.uri.toString()); - }); - - }); + await rpcProtocol.sync(); + let value = await provideReferences(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); + let [item] = value; + assert.deepEqual(item.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + assert.equal(item.uri.toString(), model.uri.toString()); }); - test('References, evil provider', function () { + test('References, evil provider', async () => { disposables.push(extHost.registerReferenceProvider(defaultExtension, defaultSelector, new class implements vscode.ReferenceProvider { provideReferences(): any { @@ -647,18 +578,14 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return provideReferences(model, new EditorPosition(1, 2), CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - - }); + await rpcProtocol.sync(); + const value = await provideReferences(model, new EditorPosition(1, 2), CancellationToken.None); + assert.equal(value.length, 1); }); // --- quick fix - test('Quick Fix, command data conversion', function () { + test('Quick Fix, command data conversion', async () => { disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.Command[] { @@ -669,20 +596,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { - assert.equal(value.length, 2); - - const [first, second] = value; - assert.equal(first.title, 'Testing1'); - assert.equal(first.command.id, 'test1'); - assert.equal(second.title, 'Testing2'); - assert.equal(second.command.id, 'test2'); - }); - }); + await rpcProtocol.sync(); + let value = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + assert.equal(value.length, 2); + const [first, second] = value; + assert.equal(first.title, 'Testing1'); + assert.equal(first.command.id, 'test1'); + assert.equal(second.title, 'Testing2'); + assert.equal(second.command.id, 'test2'); }); - test('Quick Fix, code action data conversion', function () { + test('Quick Fix, code action data conversion', async () => { disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { provideCodeActions(): vscode.CodeAction[] { @@ -696,21 +620,18 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - - const [first] = value; - assert.equal(first.title, 'Testing1'); - assert.equal(first.command.title, 'Testing1Command'); - assert.equal(first.command.id, 'test1'); - assert.equal(first.kind, 'test.scope'); - }); - }); + await rpcProtocol.sync(); + let value = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + assert.equal(value.length, 1); + const [first] = value; + assert.equal(first.title, 'Testing1'); + assert.equal(first.command.title, 'Testing1Command'); + assert.equal(first.command.id, 'test1'); + assert.equal(first.kind, 'test.scope'); }); - test('Cannot read property \'id\' of undefined, #29469', function () { + test('Cannot read property \'id\' of undefined, #29469', async () => { disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { provideCodeActions(): any { @@ -722,14 +643,12 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + assert.equal(value.length, 1); }); - test('Quick Fix, evil provider', function () { + test('Quick Fix, evil provider', async () => { disposables.push(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { provideCodeActions(): any { @@ -742,16 +661,14 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await getCodeActions(model, model.getFullModelRange(), { type: 'manual' }, CancellationToken.None); + assert.equal(value.length, 1); }); // --- navigate types - test('Navigate types, evil provider', function () { + test('Navigate types, evil provider', async () => { disposables.push(extHost.registerWorkspaceSymbolProvider(defaultExtension, new class implements vscode.WorkspaceSymbolProvider { provideWorkspaceSymbols(): any { @@ -765,21 +682,18 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return getWorkspaceSymbols('').then(value => { - assert.equal(value.length, 1); - const [first] = value; - const [, symbols] = first; - assert.equal(symbols.length, 1); - assert.equal(symbols[0].name, 'testing'); - }); - }); + await rpcProtocol.sync(); + let value = await getWorkspaceSymbols(''); + assert.equal(value.length, 1); + const [first] = value; + const [, symbols] = first; + assert.equal(symbols.length, 1); + assert.equal(symbols[0].name, 'testing'); }); // --- rename - test('Rename, evil provider 0/2', function () { + test('Rename, evil provider 0/2', async () => { disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { provideRenameEdits(): any { @@ -787,17 +701,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return rename(model, new EditorPosition(1, 1), 'newName').then(value => { - throw Error(); - }, err => { - // expected - }); - }); + await rpcProtocol.sync(); + try { + await rename(model, new EditorPosition(1, 1), 'newName'); + throw Error(); + } + catch (err) { + // expected + } }); - test('Rename, evil provider 1/2', function () { + test('Rename, evil provider 1/2', async () => { disposables.push(extHost.registerRenameProvider(defaultExtension, defaultSelector, new class implements vscode.RenameProvider { provideRenameEdits(): any { @@ -805,15 +719,12 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return rename(model, new EditorPosition(1, 1), 'newName').then(value => { - assert.equal(value.rejectReason, 'evil'); - }); - }); + await rpcProtocol.sync(); + const value = await rename(model, new EditorPosition(1, 1), 'newName'); + assert.equal(value.rejectReason, 'evil'); }); - test('Rename, evil provider 2/2', function () { + test('Rename, evil provider 2/2', async () => { disposables.push(extHost.registerRenameProvider(defaultExtension, '*', new class implements vscode.RenameProvider { provideRenameEdits(): any { @@ -829,15 +740,12 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return rename(model, new EditorPosition(1, 1), 'newName').then(value => { - assert.equal(value.edits.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await rename(model, new EditorPosition(1, 1), 'newName'); + assert.equal(value.edits.length, 1); }); - test('Rename, ordering', function () { + test('Rename, ordering', async () => { disposables.push(extHost.registerRenameProvider(defaultExtension, '*', new class implements vscode.RenameProvider { provideRenameEdits(): any { @@ -854,20 +762,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - - return rename(model, new EditorPosition(1, 1), 'newName').then(value => { - // least relevant rename provider - assert.equal(value.edits.length, 2); - assert.equal((value.edits[0]).edits.length, 1); - assert.equal((value.edits[1]).edits.length, 1); - }); - }); + await rpcProtocol.sync(); + const value = await rename(model, new EditorPosition(1, 1), 'newName'); + // least relevant rename provider + assert.equal(value.edits.length, 2); + assert.equal((value.edits[0]).edits.length, 1); + assert.equal((value.edits[1]).edits.length, 1); }); // --- parameter hints - test('Parameter Hints, order', function () { + test('Parameter Hints, order', async () => { disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, new class implements vscode.SignatureHelpProvider { provideSignatureHelp(): any { @@ -885,15 +790,12 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerKind: modes.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { - assert.ok(value); - }); - }); + await rpcProtocol.sync(); + const value = await provideSignatureHelp(model, new EditorPosition(1, 1), { triggerKind: modes.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None); + assert.ok(value); }); - test('Parameter Hints, evil provider', function () { + test('Parameter Hints, evil provider', async () => { disposables.push(extHost.registerSignatureHelpProvider(defaultExtension, defaultSelector, new class implements vscode.SignatureHelpProvider { provideSignatureHelp(): any { @@ -901,17 +803,14 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - - return provideSignatureHelp(model, new EditorPosition(1, 1), { triggerKind: modes.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None).then(value => { - assert.equal(value, undefined); - }); - }); + await rpcProtocol.sync(); + const value = await provideSignatureHelp(model, new EditorPosition(1, 1), { triggerKind: modes.SignatureHelpTriggerKind.Invoke, isRetrigger: false }, CancellationToken.None); + assert.equal(value, undefined); }); // --- suggestions - test('Suggest, order 1/3', function () { + test('Suggest, order 1/3', async () => { disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { provideCompletionItems(): any { @@ -925,15 +824,13 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { - assert.equal(value.length, 1); - assert.equal(value[0].completion.insertText, 'testing2'); - }); - }); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(model, new EditorPosition(1, 1), 'none'); + assert.equal(value.length, 1); + assert.equal(value[0].completion.insertText, 'testing2'); }); - test('Suggest, order 2/3', function () { + test('Suggest, order 2/3', async () => { disposables.push(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { provideCompletionItems(): any { @@ -947,15 +844,13 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { - assert.equal(value.length, 1); - assert.equal(value[0].completion.insertText, 'weak-selector'); - }); - }); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(model, new EditorPosition(1, 1), 'none'); + assert.equal(value.length, 1); + assert.equal(value[0].completion.insertText, 'weak-selector'); }); - test('Suggest, order 2/3', function () { + test('Suggest, order 2/3', async () => { disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { provideCompletionItems(): any { @@ -969,16 +864,14 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { - assert.equal(value.length, 2); - assert.equal(value[0].completion.insertText, 'strong-1'); // sort by label - assert.equal(value[1].completion.insertText, 'strong-2'); - }); - }); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(model, new EditorPosition(1, 1), 'none'); + assert.equal(value.length, 2); + assert.equal(value[0].completion.insertText, 'strong-1'); // sort by label + assert.equal(value[1].completion.insertText, 'strong-2'); }); - test('Suggest, evil provider', function () { + test('Suggest, evil provider', async () => { disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { provideCompletionItems(): any { @@ -993,15 +886,12 @@ suite('ExtHostLanguageFeatures', function () { }, [])); - return rpcProtocol.sync().then(() => { - - return provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { - assert.equal(value[0].container.incomplete, undefined); - }); - }); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(model, new EditorPosition(1, 1), 'none'); + assert.equal(value[0].container.incomplete, undefined); }); - test('Suggest, CompletionList', function () { + test('Suggest, CompletionList', async () => { disposables.push(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { provideCompletionItems(): any { @@ -1009,50 +899,44 @@ suite('ExtHostLanguageFeatures', function () { } }, [])); - return rpcProtocol.sync().then(() => { - - provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { - assert.equal(value[0].container.incomplete, true); - }); + await rpcProtocol.sync(); + provideSuggestionItems(model, new EditorPosition(1, 1), 'none').then(value => { + assert.equal(value[0].container.incomplete, true); }); }); // --- format - test('Format Doc, data conversion', function () { + test('Format Doc, data conversion', async () => { disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider { provideDocumentFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing'), types.TextEdit.setEndOfLine(types.EndOfLine.LF)]; } })); - return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { - assert.equal(value.length, 2); - let [first, second] = value; - assert.equal(first.text, 'testing'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - - assert.equal(second.eol, EndOfLineSequence.LF); - assert.equal(second.text, ''); - assert.deepEqual(second.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - }); - }); + await rpcProtocol.sync(); + let value = await getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + assert.equal(value.length, 2); + let [first, second] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); + assert.equal(second.eol, EndOfLineSequence.LF); + assert.equal(second.text, ''); + assert.deepEqual(second.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); - test('Format Doc, evil provider', function () { + test('Format Doc, evil provider', async () => { disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider { provideDocumentFormattingEdits(): any { throw new Error('evil'); } })); - return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); - }); + await rpcProtocol.sync(); + return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); - test('Format Doc, order', function () { + test('Format Doc, order', async () => { disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider { provideDocumentFormattingEdits(): any { @@ -1072,34 +956,30 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - assert.equal(first.text, 'testing'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - }); - }); + await rpcProtocol.sync(); + let value = await getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); - test('Format Range, data conversion', function () { + test('Format Range, data conversion', async () => { disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'testing')]; } })); - return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - assert.equal(first.text, 'testing'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - }); - }); + await rpcProtocol.sync(); + let value = await getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'testing'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); - test('Format Range, + format_doc', function () { + test('Format Range, + format_doc', async () => { disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider { provideDocumentRangeFormattingEdits(): any { return [new types.TextEdit(new types.Range(0, 0, 0, 0), 'range')]; @@ -1115,32 +995,29 @@ suite('ExtHostLanguageFeatures', function () { return [new types.TextEdit(new types.Range(0, 0, 1, 1), 'doc')]; } })); - return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - assert.equal(first.text, 'range2'); - assert.equal(first.range.startLineNumber, 3); - assert.equal(first.range.startColumn, 4); - assert.equal(first.range.endLineNumber, 5); - assert.equal(first.range.endColumn, 6); - }); - }); + await rpcProtocol.sync(); + let value = await getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, 'range2'); + assert.equal(first.range.startLineNumber, 3); + assert.equal(first.range.startColumn, 4); + assert.equal(first.range.endLineNumber, 5); + assert.equal(first.range.endColumn, 6); }); - test('Format Range, evil provider', function () { + test('Format Range, evil provider', async () => { disposables.push(extHost.registerDocumentRangeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentRangeFormattingEditProvider { provideDocumentRangeFormattingEdits(): any { throw new Error('evil'); } })); - return rpcProtocol.sync().then(() => { - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); - }); + await rpcProtocol.sync(); + return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); - test('Format on Type, data conversion', function () { + test('Format on Type, data conversion', async () => { disposables.push(extHost.registerOnTypeFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.OnTypeFormattingEditProvider { provideOnTypeFormattingEdits(): any { @@ -1148,18 +1025,15 @@ suite('ExtHostLanguageFeatures', function () { } }, [';'])); - return rpcProtocol.sync().then(() => { - return getOnTypeFormattingEdits(model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }).then(value => { - assert.equal(value.length, 1); - let [first] = value; - - assert.equal(first.text, ';'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); - }); - }); + await rpcProtocol.sync(); + let value = await getOnTypeFormattingEdits(model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.text, ';'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }); }); - test('Links, data conversion', function () { + test('Links, data conversion', async () => { disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider { provideDocumentLinks() { @@ -1167,18 +1041,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getLinks(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - - assert.equal(first.url, 'foo:bar#3'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); - }); - }); + await rpcProtocol.sync(); + let value = await getLinks(model, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.url, 'foo:bar#3'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); }); - test('Links, evil provider', function () { + test('Links, evil provider', async () => { disposables.push(extHost.registerDocumentLinkProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentLinkProvider { provideDocumentLinks() { @@ -1192,18 +1063,15 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getLinks(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - - assert.equal(first.url, 'foo:bar#3'); - assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); - }); - }); + await rpcProtocol.sync(); + let value = await getLinks(model, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.equal(first.url, 'foo:bar#3'); + assert.deepEqual(first.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 2, endColumn: 2 }); }); - test('Document colors, data conversion', function () { + test('Document colors, data conversion', async () => { disposables.push(extHost.registerColorProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentColorProvider { provideDocumentColors(): vscode.ColorInformation[] { @@ -1214,20 +1082,17 @@ suite('ExtHostLanguageFeatures', function () { } })); - return rpcProtocol.sync().then(() => { - return getColors(model, CancellationToken.None).then(value => { - assert.equal(value.length, 1); - let [first] = value; - - assert.deepEqual(first.colorInfo.color, { red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4 }); - assert.deepEqual(first.colorInfo.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 21 }); - }); - }); + await rpcProtocol.sync(); + let value = await getColors(model, CancellationToken.None); + assert.equal(value.length, 1); + let [first] = value; + assert.deepEqual(first.colorInfo.color, { red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4 }); + assert.deepEqual(first.colorInfo.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 21 }); }); // -- selection ranges - test('Selection Ranges, data conversion', async function () { + test('Selection Ranges, data conversion', async () => { disposables.push(extHost.registerSelectionRangeProvider(defaultExtension, defaultSelector, new class implements vscode.SelectionRangeProvider { provideSelectionRanges() { return [ From 3ff312e36c93d34270e18a7a643d6e4818ef8a72 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 15:38:05 -0800 Subject: [PATCH 0500/1066] Use CodeActionKind.intersects internally --- src/vs/editor/contrib/codeAction/codeAction.ts | 8 ++++---- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 4 ++-- src/vs/editor/contrib/codeAction/codeActionTrigger.ts | 8 ++++++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index b894b9dd3b8..aefd24e10fb 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -33,10 +33,10 @@ export function getCodeActions( } // Avoid calling providers that we know will not return code actions of interest - return provider.providedCodeActionKinds.some(providedKind => { + return provider.providedCodeActionKinds.map(kind => new CodeActionKind(kind)).some(providedKind => { // Filter out actions by kind // The provided kind can be either a subset of a superset of the filtered kind - if (trigger.filter && trigger.filter.kind && !(trigger.filter.kind.contains(providedKind) || new CodeActionKind(providedKind).contains(trigger.filter.kind.value))) { + if (trigger.filter && trigger.filter.kind && !trigger.filter.kind.intersects(providedKind)) { return false; } @@ -71,11 +71,11 @@ export function getCodeActions( function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean { return action - && isValidActionKind(filter, action.kind) + && isValidActionKind(filter, action.kind ? new CodeActionKind(action.kind) : undefined) && (filter && filter.onlyIncludePreferredActions ? !!action.isPreferred : true); } -function isValidActionKind(filter: CodeActionFilter | undefined, kind: string | undefined): boolean { +function isValidActionKind(filter: CodeActionFilter | undefined, kind: CodeActionKind | undefined): boolean { // Filter out actions by kind if (filter && filter.kind && (!kind || !filter.kind.contains(kind))) { return false; diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 8194d0c4f07..ee1e733a267 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -302,7 +302,7 @@ export class RefactorAction extends EditorAction { }); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), - { kind: CodeActionKind.Refactor.contains(args.kind.value) ? args.kind : CodeActionKind.Empty }, + { kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty }, args.apply); } } @@ -335,7 +335,7 @@ export class SourceAction extends EditorAction { }); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.source.noneMessage', "No source actions available"), - { kind: CodeActionKind.Source.contains(args.kind.value) ? args.kind : CodeActionKind.Empty, includeSourceActions: true }, + { kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty, includeSourceActions: true }, args.apply); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index 305470be4a4..8104fab9851 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -18,8 +18,12 @@ export class CodeActionKind { public readonly value: string ) { } - public contains(other: string): boolean { - return this.value === other || startsWith(other, this.value + CodeActionKind.sep); + public contains(other: CodeActionKind): boolean { + return this.value === other.value || startsWith(other.value, this.value + CodeActionKind.sep); + } + + public intersects(other: CodeActionKind): boolean { + return this.contains(other) || other.contains(this); } } From cc0be8db2a59f42370247dba3f4b057bc916bcb2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 16:18:01 -0800 Subject: [PATCH 0501/1066] Extract code action filtering logic to own files --- .../editor/contrib/codeAction/codeAction.ts | 48 ++++--------------- .../contrib/codeAction/codeActionTrigger.ts | 42 ++++++++++++++++ 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index aefd24e10fb..9bd587ee841 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, mergeSort, isNonEmptyArray } from 'vs/base/common/arrays'; +import { flatten, isNonEmptyArray, mergeSort } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; @@ -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 { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger'; +import { CodeActionKind, CodeActionTrigger, filtersAction, mayIncludeActionsOfKind } from './codeActionTrigger'; export function getCodeActions( model: ITextModel, @@ -21,39 +21,29 @@ export function getCodeActions( trigger: CodeActionTrigger, token: CancellationToken ): Promise { + const filter = trigger.filter || {}; + const codeActionContext: CodeActionContext = { - only: trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined, + only: filter.kind ? filter.kind.value : undefined, trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic }; const promises = CodeActionProviderRegistry.all(model) + // Avoid calling 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; } - // Avoid calling providers that we know will not return code actions of interest - return provider.providedCodeActionKinds.map(kind => new CodeActionKind(kind)).some(providedKind => { - // Filter out actions by kind - // The provided kind can be either a subset of a superset of the filtered kind - if (trigger.filter && trigger.filter.kind && !trigger.filter.kind.intersects(providedKind)) { - return false; - } - - // Don't return source actions unless they are explicitly requested - if (CodeActionKind.Source.contains(providedKind) && (!trigger.filter || !trigger.filter.includeSourceActions)) { - return false; - } - - 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 => isValidAction(trigger.filter, action)); + return providedCodeActions.filter(action => action && filtersAction(filter, action)); }, (err): CodeAction[] => { if (isPromiseCanceledError(err)) { throw err; @@ -69,26 +59,6 @@ export function getCodeActions( .then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator)); } -function isValidAction(filter: CodeActionFilter | undefined, action: CodeAction): boolean { - return action - && isValidActionKind(filter, action.kind ? new CodeActionKind(action.kind) : undefined) - && (filter && filter.onlyIncludePreferredActions ? !!action.isPreferred : true); -} - -function isValidActionKind(filter: CodeActionFilter | undefined, kind: CodeActionKind | undefined): boolean { - // Filter out actions by kind - if (filter && filter.kind && (!kind || !filter.kind.contains(kind))) { - return false; - } - - // Don't return source actions unless they are explicitly requested - if (kind && CodeActionKind.Source.contains(kind) && (!filter || !filter.includeSourceActions)) { - return false; - } - - return true; -} - function codeActionsComparator(a: CodeAction, b: CodeAction): number { if (isNonEmptyArray(a.diagnostics)) { if (isNonEmptyArray(b.diagnostics)) { diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index 8104fab9851..1332c015bd3 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { startsWith } from 'vs/base/common/strings'; +import { CodeAction } from 'vs/editor/common/modes'; export class CodeActionKind { private static readonly sep = '.'; @@ -40,6 +41,47 @@ export interface CodeActionFilter { readonly onlyIncludePreferredActions?: boolean; } +export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind: CodeActionKind): boolean { + // A provided kind may be a subset or superset of our filtered kind. + if (filter.kind && !filter.kind.intersects(providedKind)) { + return false; + } + + // Don't return source actions unless they are explicitly requested + if (CodeActionKind.Source.contains(providedKind) && !filter.includeSourceActions) { + return false; + } + + return true; +} + + +export function filtersAction(filter: CodeActionFilter, action: CodeAction): boolean { + const actionKind = action.kind ? new CodeActionKind(action.kind) : undefined; + + // Filter out actions by kind + if (filter.kind) { + if (!actionKind || !filter.kind.contains(actionKind)) { + return false; + } + } + + // Don't return source actions unless they are explicitly requested + if (!filter.includeSourceActions) { + if (actionKind && CodeActionKind.Source.contains(actionKind)) { + return false; + } + } + + if (filter.onlyIncludePreferredActions) { + if (!action.isPreferred) { + return false; + } + } + + return true; +} + export interface CodeActionTrigger { readonly type: 'auto' | 'manual'; readonly filter?: CodeActionFilter; From 2d79ffe0cd84515a4c771fcbd7294c2448d97826 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 22 Jan 2019 16:37:38 -0800 Subject: [PATCH 0502/1066] Expand code action range to entire document range when source actions are requested without any active selection Fixes #53525 --- .../editor/contrib/codeAction/codeAction.ts | 4 ++ .../codeAction/test/codeAction.test.ts | 46 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/vs/editor/contrib/codeAction/codeAction.ts b/src/vs/editor/contrib/codeAction/codeAction.ts index 9bd587ee841..5282fbb9dc7 100644 --- a/src/vs/editor/contrib/codeAction/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/codeAction.ts @@ -28,6 +28,10 @@ export function getCodeActions( trigger: trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic }; + if (filter.kind && CodeActionKind.Source.contains(filter.kind) && rangeOrSelection.isEmpty()) { + rangeOrSelection = model.getFullModelRange(); + } + const promises = CodeActionProviderRegistry.all(model) // Avoid calling providers that we know will not return code actions of interest .filter(provider => { diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index c19f65ceb69..060356ae9f0 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -12,6 +12,7 @@ import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger'; import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ITextModel } from 'vs/editor/common/model'; suite('CodeAction', () => { @@ -216,4 +217,49 @@ suite('CodeAction', () => { assert.strictEqual(actions.length, 0); assert.strictEqual(wasInvoked, false); }); + + test('getCodeActions requests for source actions should expand source actions range to entire document #53525', async function () { + const provider = new class implements CodeActionProvider { + provideCodeActions(model: ITextModel, range: Range): CodeAction[] { + return [{ + title: rangeToString(range), + kind: CodeActionKind.Source.value, + }]; + } + }; + + disposables.push(CodeActionProviderRegistry.register('fooLang', provider)); + + { + const actions = await getCodeActions(model, new Range(1, 1, 1, 1), { + type: 'manual', + filter: { + kind: CodeActionKind.Source, + includeSourceActions: true, + } + }, CancellationToken.None); + assert.strictEqual(actions.length, 1); + assert.strictEqual(actions[0].title, rangeToString(model.getFullModelRange())); + } + + { + const range = new Range(1, 1, 1, 2); + + // But we should not expand for non-empty selections + const actions = await getCodeActions(model, range, { + type: 'manual', + filter: { + kind: CodeActionKind.Source, + includeSourceActions: true, + } + }, CancellationToken.None); + assert.strictEqual(actions.length, 1); + assert.strictEqual(actions[0].title, rangeToString(range)); + } + }); }); + +function rangeToString(range: Range): string { + return `${range.startLineNumber},${range.startColumn} ${range.endLineNumber},${range.endColumn} `; +} + From d372fa0697992a4bc3b7af8b07737e17cea2d383 Mon Sep 17 00:00:00 2001 From: Phil Marshall Date: Mon, 21 Jan 2019 23:25:05 -0600 Subject: [PATCH 0503/1066] 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 0504/1066] 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 11f3201d35a61695778c48108fa0108f132eb000 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 23 Jan 2019 08:40:15 +0100 Subject: [PATCH 0505/1066] #66907 :lipstick: --- .../configuration/node/configuration.ts | 19 ++++++++++++------- .../node/configurationService.ts | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index b789593e4c9..e219d2882bc 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -18,7 +18,7 @@ import { isLinux } from 'vs/base/common/platform'; import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; -import { IStoredWorkspaceFolder, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import * as extfs from 'vs/base/node/extfs'; import { JSONEditingService } from 'vs/workbench/services/configuration/node/jsonEditingService'; import { WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; @@ -29,6 +29,11 @@ import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationModel } from 'vs/platform/configuration/common/configuration'; +export interface IWorkspaceIdentifier { + id: string; + configPath: URI; +} + export class WorkspaceConfiguration extends Disposable { private readonly _cachedConfiguration: CachedWorkspaceConfiguration; @@ -75,7 +80,7 @@ export class WorkspaceConfiguration extends Disposable { setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise { if (this._workspaceIdentifier) { - return jsonEditingService.write(URI.file(this._workspaceIdentifier.configPath), { key: 'folders', value: folders }, true) + return jsonEditingService.write(this._workspaceIdentifier.configPath, { key: 'folders', value: folders }, true) .then(() => this.reload()); } return Promise.resolve(); @@ -102,7 +107,7 @@ export class WorkspaceConfiguration extends Disposable { } return false; } - if (URI.file(this._workspaceIdentifier.configPath).scheme === Schemas.file) { + if (this._workspaceIdentifier.configPath.scheme === Schemas.file) { if (!(this._workspaceConfiguration instanceof NodeBasedWorkspaceConfiguration)) { dispose(this._workspaceConfiguration); this._workspaceConfiguration = new NodeBasedWorkspaceConfiguration(); @@ -120,7 +125,7 @@ export class WorkspaceConfiguration extends Disposable { } private updateCache(): Promise { - if (this._workspaceIdentifier && URI.file(this._workspaceIdentifier.configPath).scheme !== Schemas.file && this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration) { + if (this._workspaceIdentifier && this._workspaceIdentifier.configPath.scheme !== Schemas.file && this._workspaceConfiguration instanceof FileServiceBasedWorkspaceConfiguration) { return this._workspaceConfiguration.load(this._workspaceIdentifier) .then(() => this._cachedConfiguration.updateWorkspace(this._workspaceIdentifier, this._workspaceConfiguration.getConfigurationModel())); } @@ -195,7 +200,7 @@ abstract class AbstractWorkspaceConfiguration extends Disposable implements IWor class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration { protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise { - return pfs.readFile(workspaceIdentifier.configPath) + return pfs.readFile(workspaceIdentifier.configPath.fsPath) .then(contents => contents.toString(), e => { errors.onUnexpectedError(e); return ''; @@ -211,13 +216,13 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat constructor(private fileService: IFileService, from?: AbstractWorkspaceConfiguration) { super(from); - this.workspaceConfig = from ? URI.file(from.workspaceIdentifier.configPath) : null; + this.workspaceConfig = from ? from.workspaceIdentifier.configPath : null; this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e))); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50)); } protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise { - this.workspaceConfig = URI.file(workspaceIdentifier.configPath); + this.workspaceConfig = workspaceIdentifier.configPath; return this.fileService.resolveContent(this.workspaceConfig) .then(content => content.value, e => { errors.onUnexpectedError(e); diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index 1ce7ae68da7..1148a763638 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -339,7 +339,7 @@ export class WorkspaceService extends Disposable implements IWorkspaceConfigurat } private createMultiFolderWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { - return this.workspaceConfiguration.load(workspaceIdentifier) + 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))); From 1b777a5173b64fa60a8821c6fd978190759a54dd Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 22 Jan 2019 23:43:09 -0800 Subject: [PATCH 0506/1066] Add "Start without debugging" button to touch bar --- .../debug/electron-browser/debug.contribution.ts | 1 + .../media/continue-without-debugging-tb.png | Bin 0 -> 363 bytes 2 files changed, 1 insertion(+) create mode 100644 src/vs/workbench/parts/debug/electron-browser/media/continue-without-debugging-tb.png diff --git a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts index 7eac92e5ffc..2d640f1a84c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debug.contribution.ts @@ -451,6 +451,7 @@ if (isMacintosh) { }; registerTouchBarEntry(StartAction.ID, StartAction.LABEL, 0, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-tb.png'); + registerTouchBarEntry(RunAction.ID, RunAction.LABEL, 1, CONTEXT_IN_DEBUG_MODE.toNegated(), 'continue-without-debugging-tb.png'); registerTouchBarEntry(ContinueAction.ID, ContinueAction.LABEL, 0, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'continue-tb.png'); registerTouchBarEntry(PauseAction.ID, PauseAction.LABEL, 1, ContextKeyExpr.and(CONTEXT_IN_DEBUG_MODE, ContextKeyExpr.notEquals('debugState', 'stopped')), 'pause-tb.png'); registerTouchBarEntry(StepOverAction.ID, StepOverAction.LABEL, 2, CONTEXT_DEBUG_STATE.isEqualTo('stopped'), 'stepover-tb.png'); diff --git a/src/vs/workbench/parts/debug/electron-browser/media/continue-without-debugging-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/continue-without-debugging-tb.png new file mode 100644 index 0000000000000000000000000000000000000000..f42e02ee0c0b5e1784f33574d88624e922b85598 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIR;M`$B+ufx7RQ7HaQ5iJiN@cg?UYYR-kl< z)djwo^E*0I?mj*sahUJ3s*T2;o__+qogHBC;=9X}8U4OdT$L|BU0k9%VSBXQtrkDK zM0LG=d}f83hkfq!@`b|xlRm@?$1ndW@s;uHxg*sTlAfkJQn5Zbnc vM>%n|ftY&YX`Z9rH=6t*Xa>X=0yYd$0j+bMG|1frh7E(KtDnm{r-UW|+ Date: Wed, 23 Jan 2019 00:02:42 -0800 Subject: [PATCH 0507/1066] 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 fd8dd511d328deadd63ec15b2e1d1b77960fc2fd Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 23 Jan 2019 09:15:27 +0100 Subject: [PATCH 0508/1066] fixes #66876 --- src/vs/platform/url/common/urlService.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/url/common/urlService.ts b/src/vs/platform/url/common/urlService.ts index d57b6bf109d..a08ae07d15a 100644 --- a/src/vs/platform/url/common/urlService.ts +++ b/src/vs/platform/url/common/urlService.ts @@ -7,10 +7,7 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI } from 'vs/base/common/uri'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { first } from 'vs/base/common/async'; - -declare module Array { - function from(set: Set): T[]; -} +import { values } from 'vs/base/common/map'; export class URLService implements IURLService { @@ -19,7 +16,7 @@ export class URLService implements IURLService { private handlers = new Set(); open(uri: URI): Promise { - const handlers = Array.from(this.handlers); + const handlers = values(this.handlers); return first(handlers.map(h => () => h.handleURL(uri)), undefined, false).then(val => val || false); } From 06e4e227e88fb3f7e448bc967ab7458e45323ecc Mon Sep 17 00:00:00 2001 From: Rudi Chen Date: Wed, 23 Jan 2019 00:18:02 -0800 Subject: [PATCH 0509/1066] 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 f620b3df490ad36876117d4c6fefe51ea023f5be Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 23 Jan 2019 09:18:13 +0100 Subject: [PATCH 0510/1066] fixes #66878 --- src/vs/base/browser/dom.ts | 5 ----- src/vs/base/browser/ui/list/listView.ts | 5 +++-- src/vs/base/browser/ui/tree/abstractTree.ts | 5 +++-- src/vs/base/common/async.ts | 8 ++------ 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 868b77ade0e..2ac95d2b9c6 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1167,8 +1167,3 @@ export function animate(fn: () => void): IDisposable { let stepDisposable = scheduleAtNextAnimationFrame(step); return toDisposable(() => stepDisposable.dispose()); } - -export function timeout(fn: () => void, millis: number): IDisposable { - const timer = setTimeout(fn, millis); - return toDisposable(() => clearTimeout(timer)); -} \ No newline at end of file diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 844e080672f..153b169432a 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -21,6 +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'; function canUseTranslate3d(): boolean { if (browser.isFirefox) { @@ -731,7 +732,7 @@ export class ListView implements ISpliceable, IDisposable { private onDragLeave(): void { this.onDragLeaveTimeout.dispose(); - this.onDragLeaveTimeout = DOM.timeout(() => this.clearDragOverFeedback(), 100); + this.onDragLeaveTimeout = disposableTimeout(() => this.clearDragOverFeedback(), 100); } private onDrop(event: IListDragEvent): void { @@ -777,7 +778,7 @@ export class ListView implements ISpliceable, IDisposable { } this.dragOverAnimationStopDisposable.dispose(); - this.dragOverAnimationStopDisposable = DOM.timeout(() => { + this.dragOverAnimationStopDisposable = disposableTimeout(() => { if (this.dragOverAnimationDisposable) { this.dragOverAnimationDisposable.dispose(); this.dragOverAnimationDisposable = undefined; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1bc2f89ad9b..16999aee9d3 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, timeout, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -20,6 +20,7 @@ import { domEvent } from 'vs/base/browser/event'; 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'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -69,7 +70,7 @@ class TreeNodeListDragAndDrop implements IListDragAndDrop< } if (didChangeAutoExpandNode && typeof result !== 'boolean' && result.autoExpand) { - this.autoExpandDisposable = timeout(() => { + this.autoExpandDisposable = disposableTimeout(() => { const model = this.modelProvider(); const ref = model.getNodeLocation(targetNode); diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 9660a6cf966..807898c6989 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,7 +6,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as errors from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; export function isThenable(obj: any): obj is Promise { @@ -320,11 +320,7 @@ export function timeout(millis: number, token?: CancellationToken): CancelablePr export function disposableTimeout(handler: () => void, timeout = 0): IDisposable { const timer = setTimeout(handler, timeout); - return { - dispose() { - clearTimeout(timer); - } - }; + return toDisposable(() => clearTimeout(timer)); } /** From 9ad7e6a45c65ea211af651737eb12a25d5b85323 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 23 Jan 2019 09:24:53 +0100 Subject: [PATCH 0511/1066] fixes #66896 --- src/vs/base/browser/ui/list/list.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 0466517aa67..cf486cb94e9 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -186,4 +186,14 @@ .monaco-list-type-filter-message:empty { display: none; +} + +/* Electron */ + +.monaco-list-type-filter { + cursor: -webkit-grab; +} + +.monaco-list-type-filter.dragging { + cursor: -webkit-grabbing; } \ No newline at end of file From 3117f30f3d6b6d9f3174241bebe2373e58ab7ad3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 23 Jan 2019 09:34:51 +0100 Subject: [PATCH 0512/1066] remove bottom corners of filter type widget fixes #66897 --- src/vs/base/browser/ui/list/list.css | 13 +----------- src/vs/base/browser/ui/tree/abstractTree.ts | 22 +++------------------ 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index cf486cb94e9..5c0cfcf3a85 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -80,6 +80,7 @@ line-height: 18px; height: 20px; z-index: 1; + top: 4px; } .monaco-list-type-filter.dragging { @@ -87,22 +88,10 @@ } .monaco-list-type-filter.ne { - top: 4px; right: 4px; } .monaco-list-type-filter.nw { - top: 4px; - left: 4px; -} - -.monaco-list-type-filter.se { - bottom: 4px; - right: 4px; -} - -.monaco-list-type-filter.sw { - bottom: 4px; left: 4px; } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 16999aee9d3..85da0ba4eb9 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -467,13 +467,10 @@ class TypeFilterController implements IDisposable { private onDragStart(): void { const container = this.view.getHTMLElement(); - const { top, left } = getDomNodePagePosition(container); + const { left } = getDomNodePagePosition(container); const containerWidth = container.clientWidth; - const containerHeight = container.clientHeight; const midContainerWidth = containerWidth / 2; - const midContainerHeight = containerHeight / 2; const width = this.domNode.clientWidth; - const height = this.domNode.clientHeight; const disposables: IDisposable[] = []; let positionClassName = this.positionClassName; @@ -487,29 +484,16 @@ class TypeFilterController implements IDisposable { this.domNode.style.top = `4px`; this.domNode.style.left = `${containerWidth - width - 6}px`; break; - case 'sw': - this.domNode.style.top = `${containerHeight - height - 6}px`; - this.domNode.style.left = `4px`; - break; - case 'se': - this.domNode.style.top = `${containerHeight - height - 6}px`; - this.domNode.style.left = `${containerWidth - width - 6}px`; - break; } }; const onDragOver = (event: DragEvent) => { const x = event.screenX - left; - const y = event.screenY - top; - if (x < midContainerWidth && y < midContainerHeight) { + if (x < midContainerWidth) { positionClassName = 'nw'; - } else if (x >= midContainerWidth && y < midContainerHeight) { - positionClassName = 'ne'; - } else if (x < midContainerWidth && y >= midContainerHeight) { - positionClassName = 'sw'; } else { - positionClassName = 'se'; + positionClassName = 'ne'; } updatePosition(); From cbbd49e14f60dcd2f315fc4c7387884abc8013d4 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Wed, 23 Jan 2019 02:33:35 -0800 Subject: [PATCH 0513/1066] Update touch bar icons (#66966) * Update touch bar icons * Update stepout-tb.png --- .../browser/parts/editor/media/back-tb.png | Bin 381 -> 266 bytes .../browser/parts/editor/media/forward-tb.png | Bin 381 -> 258 bytes .../electron-browser/media/continue-tb.png | Bin 435 -> 339 bytes .../debug/electron-browser/media/pause-tb.png | Bin 375 -> 217 bytes .../debug/electron-browser/media/restart-tb.png | Bin 608 -> 701 bytes .../electron-browser/media/stepinto-tb.png | Bin 484 -> 418 bytes .../debug/electron-browser/media/stepout-tb.png | Bin 478 -> 411 bytes .../electron-browser/media/stepover-tb.png | Bin 638 -> 548 bytes .../debug/electron-browser/media/stop-tb.png | Bin 362 -> 197 bytes 9 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/back-tb.png b/src/vs/workbench/browser/parts/editor/media/back-tb.png index 88041b756d3135c4c2807187ced07bbbb4ddc0c5..c9d6f935ae4b37dc1dca6f5c14540e0916c715e0 100644 GIT binary patch delta 257 zcmey%)Wzi78Q|y6%O%Cdz`(%k>ERLtq-{W$gAGVp{kd5Lq&N#aB8wRqxP?KOkzv*x z37{Zjage(c!@6@aFDEJr*U$HKaSW+oe0$>{XS0KZ%R^5s56)$qmQ0>9N$8TN*9E^n zcU@ftziYhuVJGq7*96-)UO*EV7-ZaCp6tE!FO%O=I_bXQ$KJS3*}qR#2t+RrEi#@H zne?*?y}vd$@?2>_bAU_1Z- literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^DIm z%83feO;IT&TrW3G6VVJ>;xSRl_0&4e^yyOquPUZq`&X2+PZVqdL;P+=?sK&XpNt+! z0tHk{Tq8?MniK&&bnYMv}m4QLU`={+F8glbf YGSez?Yv9}zYYno})78&qol`;+05vIW<^TWy diff --git a/src/vs/workbench/browser/parts/editor/media/forward-tb.png b/src/vs/workbench/browser/parts/editor/media/forward-tb.png index 03327b9685ea6e2db58a60ee22027fe474e1000a..69c52268ab8035d106013488bd08cff7e8682733 100644 GIT binary patch delta 249 zcmey%)Wqc68Q|y6%O%Cdz`(%k>ERLtq-{W$gAGVp{kd5Lq&N#aB8wRqxP?KOkzv*x z37{Zjage(c!@6@aFDEJr*H8C!aSW+oe0y^v?;!^shl|3(2242zo@`*uVNf<;G&uB7 zW8s1YJ^^pOtMGq#I(c0|HqZov`S*UCJrgR> t-Yn=}J}tZRM!)8-7$q?VhK8+`%rUVycTBHNv_WkUJ?KR literal 381 zcmeAS@N?(olHy`uVBq!ia0vp^DImK zbGA5eeRkq#d95rHb#zwZhDq|mPfk0mSx z3aFO2MwFx^mZVxG7o`Fz1|tJQ3tdA)T?4ZaBLgcFQ!8ULZ36=<1A~h9Puo#64nJa0`PlBg3pY z5pp9!lefdRLiZsr`@ U@<|p~fI-0E>FVdQ&MBb@04ni^a{vGU literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^DImiqXniaT;>EY)Yck3cgA6)k;@H|jG2z=0gqJ2`c{oe%nw}wC0TB>c2 zN-pwG_L0l%K56W0Sh-YHE!yNr%Id4hpZcaIe_D4q&dKh(K+(C4^-e@ zM@~GW%xCJo%kZDJ|{O#%6zRh zn*LeY?$ ZpOTqYiCaTtuUZ<&N>5immvv4FO#p_jh(7=T diff --git a/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/pause-tb.png index 51089a3b699a7a749f6bacf9d2700c16b1800be1..f564b78761f723604eabc4d4f4de6cc38b1357fd 100644 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBex9d`V@L(#+w-S64+rqLItpI$u=37)sWUfX z+rjAn57w?rchFWmxRQy1p`mcs@9V$+sc(N>!}o4^>ORv0tL5AZtPZ|pSuQ-s4+~@0 ran}9WbMB?(mw!Ee_dd|Bg6lj6%3hx*t4XZ|@)$f_{an^LB{Ts5+Z;=* literal 375 zcmeAS@N?(olHy`uVBq!ia0vp^DImq8+jWHcw7(m z9N>^_6x>vQn4@Qcpbz_!jwePz?_&NPTQ34s3zaj*u&d80LF#0wzO9+p{c<_ZgNU%etVubH#d!oCq0~2c>41#-6`u8Ywom{qWXbjN^v{K)Oa$oq;OGM4#W~I0{tb^b4*RZ zn9DD6K3@Yd3)UA;9GTP87BU*9y30fMm|h{0*o6lTG+ARmSA{Ll#rGJFE0<%|C9Ayg z*M@b%F}9cj8c#JMw&<g`w}_95A?Cmbf0nB7rti!v!9R~{7^^K>|1@T)Oew~Roz*~ z)Sa^}60<}CqPB-V^uc7V%>qr(|Nrrih+SK*we)^q~g}wo3S&S4Fp;r zdN??#Dll?-I4}umfhZ@H5GR!*2XA(_+URnBd7swy_ujp4O`lWLIGs6wCPKiErggjK zDY`9Pv*xtqHRnmo1VXX{uIydmxVB$n`lkz?)sw11uQm5n`5NstS{2?u+jLd1@qepc z{?{fqc!k0p&7$Jo-rc|Gq)_nZYBEw{eIGYPwEf5mL50yy-K>N zO=xLdqUofEFQ+XNT3UZedfIVE#T8nCOLoK>@v)VxX8N7`I5j0|x75sO+x*Y9-{6mZ z@O$&2tn^R67qY{`;y|nXgM(X^3%+~x2PmXk;u=wsl30>zm0Xkxq!^403@vmG4RsC7 wLW~ToOiZneEwl{`tPBh^F3h`yq9HdwB{QuOw}!}GwKR~Wp00i_>zopr0IT2UssI20 diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stepinto-tb.png index 3915e870ee1236890273c08e22ea30faef11b5b1..f7a4eb230138f2996c22a2af3c37bad926a0961b 100644 GIT binary patch literal 418 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIde}J$B+ufw>RE)9&!*k_VHwB!F5M1i`;{5 zM}*!eOiN%d6WYKKuN9Evu=YUVwKs=WN%tx!Tie;s;Fmi1$1w7m03*;)Ab64dOgDb^ zghledj5xOO9lWM^!-{49&-|B7dl{TplM~?j{{Gu7i*8J<&z~QR2`$VgwABK)0u=_28SmtEcjJ)2&1S?I6Nyp0FDFIFCBlU*#Xmc%T-_kY)o z1**5watefVAJ3~j=6-#Chpxv=mt*&9D+5KUmM@fglgw^pJ2NOv*LeEPEVtDU()QbU zuDKvx-#J-R$mCI(baA)%{rA(8)n^)NUZ`RC@Z$Zv-Hrb}8`C~~JZM(|Nrr3Mh2l)5S5Q;?~<6j)KhwBCZ!b zI$AtBSe7WTYI-<0sk$%;6@1v0vD{gm)hy@Udt1JDQ~u010h$Q~cf9yLFS&3iwg@-@ zi5+bkt6zEq_f2>-^`l?p?jGN(ADLI*VoE8|+$R~Yc~5fp6vIQ8SGAq!_;^NkYYpG; zq+h#N{EO&3`|d=!^^sx@ZTXZvK{a}svr5y-PHOB5ib=aF+W&LuOQRH}mI?dqlmnNA z&9I#`>D0?K{gdr&hj(9lP@endziXtg{2r9swJ)w zB`Jv|saDBFsX&Us$iUD-*U(Vcz%0bbz{{UwxS?cNP=d#Wzp$P!RJ)vm; diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stepout-tb.png index 7e31b956fbc8cd2ff1bf02c306cdd87eb05f1623..b7332b3485c5d307407221bbd156c27be978b89b 100644 GIT binary patch literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBIRj4@$B+ufw>NI*9&!+9eds)eDTpakA<)Xh zeIZNvgeeZJf|&{djH(G%3{nXG?AoOjvF_>{)1{|hY>k+I(RSm$Mfp8n1!Ftzs=nUZ{lY0n^q9@&wNkZPOupY- zy(|Nrr3Mh2Z)5S5Q;?~=nj=aqdB5a8& z3XGf{4qi(Y5>*vAIXw;>{Bosj39Fv%m-?CK52i$Z(=C~+;yDR|>U!9%lk1Lg2+Tip zg72edJ5NLJJ}0&VQZt;+A6vdfq$+IRX|Kd*cjoMR%DAFHK`N<3u}vbWBP*te{Z=*G zy$qh2l539~I8kKuTkq%UiQBb!Vx%w0ubw_lXw&_coWS&-Q>Cm`(i2t+2c`8-sm%6u zJvVXo``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBxt*Rajv*C{Z)d&@JZ!+zCffIZ3hyD7H3Di4 zoEkn1(g)Z|m|n01h%HEEeDK3xSZu-E1x~wm>=pRr`0Bx|4|e76x0dOscO|)(bpXwV z0Im~?XJpd4J}YoMGkyDYdHNQoH$tBTcRrZ+!pB7H><{axa`ji!Z>;7kGUNWl+q)#= z#Mus4@tt*l%5S)o9D8k?`uRom`wea#w-Xg#{ck&b`-;#UyT0kF?dpprz6jhhXNA+n zeJ}3(IlpM~%ib4rYh32oYIa_U5V?9o@9krz-~1OdXA5p?yUe-jMG`yjuYk8jR>{w2 z{(Sb~!m*n%@r^HLuHIO5L+j&&$=OZ%y`y|pahXx*LXA~M6aQ_`5H9$5 z!#3-N`YjtL-=`Q6@64C94V}8!FIsVOd&HF2!%l7z{#JhMlfAe8r_z+*P z_h@j5-`P1QM9&48JYKVGPWU4A#o23(kMC-=SlTE0wq;G3g%$Ji)BATQ+1I@YnR0Yy zN46NtxVA9FQ@WP^8Y ksS)LUwa<_d7RU$s{3EUgDm7MBy$1<-y85}Sb4q9e0Qvp#`~Uy| literal 638 zcmeAS@N?(olHy`uVBq!ia0vp^DIm(|Nrrih+T#(9^{+q~g}w8{Sh}3Z|!Q^Ho3lA$>&nh|9~nPXn8TxA(r#Ihne7y1C=8 znsXCh?+Se;d?fZy=9$;`ilkIUc6$EVJIh#e_eq1^U0>_sL}3BNaDe?clX`r4uRuyx zDo{|h#5JNMC9x#cD!C{XNHG{07+UBW8tNLDg%}xFnV4D`TWA{?SQ!{-T$pzYMMG|W ZN@iLmZVi#WYH1*AJzf1=);T3K0RU$N?1%sW diff --git a/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png b/src/vs/workbench/parts/debug/electron-browser/media/stop-tb.png index 54235b60d05fa521cf2486e3fb7dc9e1453ab58a..715c180ccd6d4aa9769770aa09c4e656b2577cf4 100644 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBew3$+V@L(#+nXDC8w?nj9oaYjKh-EH#M!d* zb!^RB7N82FO^NqD?Xf&&u_>eSM`+V+&OG%Ub03spWBk?oab{llxh+6l2j+3B#UO_QmvAUQh^kMk%6IwuA!l>fmw)= uft87=m9d4kfq|8QfyRY-w@@_X=BH$)RpQnV*{hZYveMJl&t;ucLK6Tst6+Kn From 6d8259cdd5781bae6ca0992342fd8ed8306013d7 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Jan 2019 11:42:45 +0100 Subject: [PATCH 0514/1066] open editors: null guard --- .../parts/files/electron-browser/views/openEditorsView.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 59efa778648..9f12476d434 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -609,7 +609,13 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop Date: Wed, 23 Jan 2019 12:19:49 +0100 Subject: [PATCH 0515/1066] explorer: If the click is outside of the elements pass the root resource if there is only one root. If there are multiple roots pass empty object. fixes #66942 --- .../parts/files/electron-browser/views/explorerView.ts | 5 ++++- 1 file changed, 4 insertions(+), 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 c9e31ba5f41..47eeceef1ad 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -371,7 +371,10 @@ export class ExplorerView extends ViewletPanel { getAnchor: () => e.anchor, getActions: () => { const actions: IAction[] = []; - fillInContextMenuActions(this.contributedContextMenu, { arg: stat instanceof ExplorerItem ? stat.resource : {}, shouldForwardArgs: true }, actions, this.contextMenuService); + // If the click is outside of the elements pass the root resource if there is only one root. If there are multiple roots pass empty object. + const roots = this.explorerService.roots; + const arg = stat instanceof ExplorerItem ? stat.resource : roots.length === 1 ? roots[0].resource : {}; + fillInContextMenuActions(this.contributedContextMenu, { arg, shouldForwardArgs: true }, actions, this.contextMenuService); return actions; }, onHide: (wasCancelled?: boolean) => { From 6220dcd9acadf16e6c779acda00f5ed28a2152e0 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 23 Jan 2019 11:20:14 +0100 Subject: [PATCH 0516/1066] separate schema for workbenchColors and textmateColors, to avoid repetition in configuration schemas --- src/vs/platform/theme/common/colorRegistry.ts | 8 +- .../electron-browser/themes.contribution.ts | 4 +- .../themes/common/colorThemeSchema.ts | 162 ++++++++++-------- .../themes/common/fileIconThemeSchema.ts | 2 +- .../themes/common/workbenchThemeService.ts | 1 - .../electron-browser/workbenchThemeService.ts | 53 +++--- 6 files changed, 130 insertions(+), 100 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 61e2b539623..1a0b67210d6 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -9,6 +9,7 @@ import { Color, RGBA } from 'vs/base/common/color'; import { ITheme } from 'vs/platform/theme/common/themeService'; import * as nls from 'vs/nls'; +import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; // ------ API types @@ -79,7 +80,7 @@ export interface IColorRegistry { class ColorRegistry implements IColorRegistry { private colorsById: { [key: string]: ColorContribution }; - private colorSchema: IJSONSchema = { type: 'object', description: nls.localize('schema.colors', "Colors used in the workbench."), properties: {}, additionalProperties: false }; + private colorSchema: IJSONSchema = { type: 'object', properties: {} }; private colorReferenceSchema: IJSONSchema = { type: 'string', enum: [], enumDescriptions: [] }; constructor() { @@ -443,6 +444,11 @@ function resolveColorValue(colorValue: ColorValue | null, theme: ITheme): Color return null; } +export const workbenchColorsSchemaId = 'vscode://schemas/workbench-colors'; + +let schemaRegistry = platform.Registry.as(JSONExtensions.JSONContribution); +schemaRegistry.registerSchema(workbenchColorsSchemaId, colorRegistry.getColorSchema()); + // setTimeout(_ => console.log(colorRegistry.toString()), 5000); diff --git a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts index 3acacb44791..7a9c717f891 100644 --- a/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts +++ b/src/vs/workbench/parts/themes/electron-browser/themes.contribution.ts @@ -21,7 +21,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { Color } from 'vs/base/common/color'; import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { LIGHT, DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; -import { schemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; +import { colorThemeSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from 'vs/platform/quickinput/common/quickInput'; @@ -216,7 +216,7 @@ class GenerateColorThemeAction extends Action { } } let contents = JSON.stringify({ - '$schema': schemaId, + '$schema': colorThemeSchemaId, type: theme.type, colors: resultingColors, tokenColors: theme.tokenColors.filter(t => !!t.scope) diff --git a/src/vs/workbench/services/themes/common/colorThemeSchema.ts b/src/vs/workbench/services/themes/common/colorThemeSchema.ts index 2536de34718..15dd9ab3630 100644 --- a/src/vs/workbench/services/themes/common/colorThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/colorThemeSchema.ts @@ -8,9 +8,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { Extensions as ThemeingExtensions, IColorRegistry } from 'vs/platform/theme/common/colorRegistry'; +import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; -let themingRegistry = Registry.as(ThemeingExtensions.ColorContribution); let textMateScopes = [ 'comment', 'comment.block', @@ -114,97 +113,110 @@ let textMateScopes = [ 'variable.parameter' ]; -export const tokenColorizationSettingSchema: IJSONSchema = { - type: 'object', - description: nls.localize('schema.token.settings', 'Colors and styles for the token.'), - properties: { - foreground: { - type: 'string', - description: nls.localize('schema.token.foreground', 'Foreground color for the token.'), - format: 'color-hex', - default: '#ff0000' - }, - background: { - type: 'string', - deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.') - }, - fontStyle: { - type: 'string', - description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets inherited settings.'), - pattern: '^(\\s*\\b(italic|bold|underline))*\\s*$', - patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' or a combination or the empty string.'), - defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] +export const textmateColorsSchemaId = 'vscode://schemas/textmate-colors'; +export const textmateColorSettingsSchemaId = `${textmateColorsSchemaId}#definitions/settings`; + +const textmateColorSchema: IJSONSchema = { + type: 'array', + definitions: { + settings: { + type: 'object', + description: nls.localize('schema.token.settings', 'Colors and styles for the token.'), + properties: { + foreground: { + type: 'string', + description: nls.localize('schema.token.foreground', 'Foreground color for the token.'), + format: 'color-hex', + default: '#ff0000' + }, + background: { + type: 'string', + deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.') + }, + fontStyle: { + type: 'string', + description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\' or a combination. The empty string unsets inherited settings.'), + pattern: '^(\\s*\\b(italic|bold|underline))*\\s*$', + patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' or a combination or the empty string.'), + defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }] + } + }, + additionalProperties: false, + defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }] } }, - additionalProperties: false, - defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }] + items: { + type: 'object', + defaultSnippets: [{ body: { scope: '${1:keyword.operator}', settings: { foreground: '${2:#FF0000}' } } }], + properties: { + name: { + type: 'string', + description: nls.localize('schema.properties.name', 'Description of the rule.') + }, + scope: { + description: nls.localize('schema.properties.scope', 'Scope selector against which this rule matches.'), + anyOf: [ + { + enum: textMateScopes + }, + { + type: 'string' + }, + { + type: 'array', + items: { + enum: textMateScopes + } + }, + { + type: 'array', + items: { + type: 'string' + } + } + ] + }, + settings: { + $ref: '#definitions/settings' + } + }, + required: [ + 'settings', 'scope' + ], + additionalProperties: false + } }; -export const colorsSchema = themingRegistry.getColorSchema(); -export function tokenColorsSchema(description: string): IJSONSchema { - return { - type: 'array', - description, - items: { - type: 'object', - defaultSnippets: [{ body: { scope: '${1:keyword.operator}', settings: { foreground: '${2:#FF0000}' } } }], - properties: { - name: { - type: 'string', - description: nls.localize('schema.properties.name', 'Description of the rule.') - }, - scope: { - description: nls.localize('schema.properties.scope', 'Scope selector against which this rule matches.'), - anyOf: [ - { - enum: textMateScopes - }, - { - type: 'string' - }, - { - type: 'array', - items: { - enum: textMateScopes - } - }, - { - type: 'array', - items: { - type: 'string' - } - } - ] - }, - settings: tokenColorizationSettingSchema, - }, - required: [ - 'settings', 'scope' - ], - additionalProperties: false - } - }; -} +export const colorThemeSchemaId = 'vscode://schemas/color-theme'; -export const schemaId = 'vscode://schemas/color-theme'; -const schema: IJSONSchema = { +const colorThemeSchema: IJSONSchema = { type: 'object', allowComments: true, properties: { - colors: colorsSchema, + colors: { + description: nls.localize('schema.workbenchColors', 'Colors in the workbench'), + $ref: workbenchColorsSchemaId, + additionalProperties: false + }, tokenColors: { anyOf: [{ type: 'string', description: nls.localize('schema.tokenColors.path', 'Path to a tmTheme file (relative to the current file).') }, - tokenColorsSchema(nls.localize('schema.colors', 'Colors for syntax highlighting')) + { + description: nls.localize('schema.colors', 'Colors for syntax highlighting'), + $ref: textmateColorsSchemaId + } ] } } }; -export function register() { + + +export function registerColorThemeSchemas() { let schemaRegistry = Registry.as(JSONExtensions.JSONContribution); - schemaRegistry.registerSchema(schemaId, schema); + schemaRegistry.registerSchema(colorThemeSchemaId, colorThemeSchema); + schemaRegistry.registerSchema(textmateColorsSchemaId, textmateColorSchema); } diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index 41795ef77f0..8327900663d 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -218,7 +218,7 @@ const schema: IJSONSchema = { } }; -export function register() { +export function registerFileIconThemeSchemas() { let schemaRegistry = Registry.as(JSONExtensions.JSONContribution); schemaRegistry.registerSchema(schemaId, schema); } \ No newline at end of file diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index 2714e963216..533d44586f5 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -22,7 +22,6 @@ export const DETECT_HC_SETTING = 'window.autoDetectHighContrast'; export const ICON_THEME_SETTING = 'workbench.iconTheme'; export const CUSTOM_WORKBENCH_COLORS_SETTING = 'workbench.colorCustomizations'; export const CUSTOM_EDITOR_COLORS_SETTING = 'editor.tokenColorCustomizations'; -export const CUSTOM_EDITOR_SCOPE_COLORS_SETTING = 'textMateRules'; export interface IColorTheme extends ITheme { readonly id: string; diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index fb7c0971fd0..fb1ffc5a0ab 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, CUSTOM_EDITOR_SCOPE_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -17,8 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ColorThemeData } from './colorThemeData'; import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService'; import { Event, Emitter } from 'vs/base/common/event'; -import * as colorThemeSchema from 'vs/workbench/services/themes/common/colorThemeSchema'; -import * as fileIconThemeSchema from 'vs/workbench/services/themes/common/fileIconThemeSchema'; +import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ColorThemeStore } from 'vs/workbench/services/themes/electron-browser/colorThemeStore'; import { FileIconThemeStore } from 'vs/workbench/services/themes/electron-browser/fileIconThemeStore'; @@ -29,7 +28,9 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService, FileChangeType } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; -import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { textmateColorsSchemaId, registerColorThemeSchemas, textmateColorSettingsSchemaId } from 'vs/workbench/services/themes/common/colorThemeSchema'; +import { workbenchColorsSchemaId } from 'vs/platform/theme/common/colorRegistry'; // implementation @@ -149,12 +150,17 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} }; + const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false }; + const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false }; for (let t of themes) { + // add a enum value to the 'workbench.colorTheme` setting colorThemeSettingSchema.enum!.push(t.settingsId); colorThemeSettingSchema.enumDescriptions!.push(t.description || ''); + + // add theme specific color customization ("[Abyss]":{ ... }) const themeId = `[${t.settingsId}]`; - themeSpecificWorkbenchColors.properties![themeId] = colorThemeSchema.colorsSchema; - themeSpecificTokenColors.properties![themeId] = { properties: tokenColorConfigurationProperties, additionalProperties: false }; + themeSpecificWorkbenchColors.properties![themeId] = workbenchColors; + themeSpecificTokenColors.properties![themeId] = tokenColors; } colorCustomizationsSchema.allOf![1] = themeSpecificWorkbenchColors; @@ -546,8 +552,8 @@ function _applyRules(styleSheetContent: string, rulesClassName: string) { } } -colorThemeSchema.register(); -fileIconThemeSchema.register(); +registerColorThemeSchemas(); +registerFileIconThemeSchemas(); class ConfigurationWriter { constructor(@IConfigurationService private readonly configurationService: IConfigurationService) { @@ -596,7 +602,7 @@ const iconThemeSettingSchema: IConfigurationPropertySchema = { const colorCustomizationsSchema: IConfigurationPropertySchema = { type: 'object', description: nls.localize('workbenchColors', "Overrides colors from the currently selected color theme."), - allOf: [{ properties: colorThemeSchema.colorsSchema.properties }], + allOf: [{ $ref: workbenchColorsSchemaId }], default: {}, defaultSnippets: [{ body: { @@ -625,25 +631,32 @@ function tokenGroupSettings(description: string) { type: 'string', format: 'color-hex' }, - colorThemeSchema.tokenColorizationSettingSchema + { + $ref: textmateColorSettingsSchemaId + } ] }; } -const tokenColorConfigurationProperties: IJSONSchemaMap = { - comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), - strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), - keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), - numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")), - types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")), - functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")), - variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), - [CUSTOM_EDITOR_SCOPE_COLORS_SETTING]: colorThemeSchema.tokenColorsSchema(nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).')) +const tokenColorSchema: IJSONSchema = { + properties: { + comments: tokenGroupSettings(nls.localize('editorColors.comments', "Sets the colors and styles for comments")), + strings: tokenGroupSettings(nls.localize('editorColors.strings', "Sets the colors and styles for strings literals.")), + keywords: tokenGroupSettings(nls.localize('editorColors.keywords', "Sets the colors and styles for keywords.")), + numbers: tokenGroupSettings(nls.localize('editorColors.numbers', "Sets the colors and styles for number literals.")), + types: tokenGroupSettings(nls.localize('editorColors.types', "Sets the colors and styles for type declarations and references.")), + functions: tokenGroupSettings(nls.localize('editorColors.functions', "Sets the colors and styles for functions declarations and references.")), + variables: tokenGroupSettings(nls.localize('editorColors.variables', "Sets the colors and styles for variables declarations and references.")), + textMateRules: { + description: nls.localize('editorColors.textMateRules', 'Sets colors and styles using textmate theming rules (advanced).'), + $ref: textmateColorsSchemaId + } + } }; const tokenColorCustomizationSchema: IConfigurationPropertySchema = { description: nls.localize('editorColors', "Overrides editor colors and font style from the currently selected color theme."), default: {}, - allOf: [{ properties: tokenColorConfigurationProperties }] + allOf: [tokenColorSchema] }; const tokenColorCustomizationConfiguration: IConfigurationNode = { id: 'editor', From 6ded24bc601f489b3946230016a92ef60819b500 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 23 Jan 2019 12:41:35 +0100 Subject: [PATCH 0517/1066] make color contribution point dynamic --- extensions/rust/package.json | 22 ++++---- src/vs/platform/theme/common/colorRegistry.ts | 52 +++++++++++++++---- .../themes/common/colorExtensionPoint.ts | 34 +++++++----- 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/extensions/rust/package.json b/extensions/rust/package.json index 4436da8bcaf..58ee4e84987 100644 --- a/extensions/rust/package.json +++ b/extensions/rust/package.json @@ -9,16 +9,16 @@ "update-grammar": "node ../../build/npm/update-grammar.js zargony/atom-language-rust grammars/rust.cson ./syntaxes/rust.tmLanguage.json" }, "contributes": { - "languages": [{ - "id": "rust", - "extensions": [".rs"], - "aliases": ["Rust", "rust"], - "configuration": "./language-configuration.json" - }], - "grammars": [{ - "language": "rust", - "path": "./syntaxes/rust.tmLanguage.json", - "scopeName":"source.rust" - }] + "colors": [ + { + "id": "rub.addedResourceForeground", + "description": "%colors.added%", + "defaults": { + "light": "#587c0c", + "dark": "#81b88b", + "highContrast": "#1b5225" + } + } + ] } } \ No newline at end of file diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 1a0b67210d6..aafd80bf811 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as platform from 'vs/platform/registry/common/platform'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { Color, RGBA } from 'vs/base/common/color'; import { ITheme } from 'vs/platform/theme/common/themeService'; +import { Event, Emitter } from 'vs/base/common/event'; import * as nls from 'vs/nls'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; +import { RunOnceScheduler } from 'vs/base/common/async'; // ------ API types @@ -46,14 +48,21 @@ export const Extensions = { export interface IColorRegistry { + readonly onDidChangeSchema: Event; + /** * Register a color to the registry. - * @param id The color id as used in theme descrition files + * @param id The color id as used in theme description files * @param defaults The default values * @description the description */ registerColor(id: string, defaults: ColorDefaults, description: string): ColorIdentifier; + /** + * Register a color to the registry. + */ + deregisterColor(id: string); + /** * Get all color contributions */ @@ -65,12 +74,12 @@ export interface IColorRegistry { resolveDefaultColor(id: ColorIdentifier, theme: ITheme): Color | null; /** - * JSON schema for an object to assign color values to one of the color contrbutions. + * JSON schema for an object to assign color values to one of the color contributions. */ getColorSchema(): IJSONSchema; /** - * JSON schema to for a reference to a color contrbution. + * JSON schema to for a reference to a color contribution. */ getColorReferenceSchema(): IJSONSchema; @@ -79,9 +88,13 @@ export interface IColorRegistry { class ColorRegistry implements IColorRegistry { + + private readonly _onDidChangeSchema = new Emitter(); + readonly onDidChangeSchema: Event = this._onDidChangeSchema.event; + private colorsById: { [key: string]: ColorContribution }; - private colorSchema: IJSONSchema = { type: 'object', properties: {} }; - private colorReferenceSchema: IJSONSchema = { type: 'string', enum: [], enumDescriptions: [] }; + private colorSchema: IJSONSchema & { properties: IJSONSchemaMap } = { type: 'object', properties: {} }; + private colorReferenceSchema: IJSONSchema & { enum: string[], enumDescriptions: string[] } = { type: 'string', enum: [], enumDescriptions: [] }; constructor() { this.colorsById = {}; @@ -94,12 +107,26 @@ class ColorRegistry implements IColorRegistry { if (deprecationMessage) { propertySchema.deprecationMessage = deprecationMessage; } - this.colorSchema.properties![id] = propertySchema; - this.colorReferenceSchema.enum!.push(id); - this.colorReferenceSchema.enumDescriptions!.push(description); + this.colorSchema.properties[id] = propertySchema; + this.colorReferenceSchema.enum.push(id); + this.colorReferenceSchema.enumDescriptions.push(description); + + this._onDidChangeSchema.fire(); return id; } + + public deregisterColor(id: string): void { + delete this.colorsById[id]; + delete this.colorSchema.properties[id]; + const index = this.colorReferenceSchema.enum.indexOf(id); + if (index !== -1) { + this.colorReferenceSchema.enum.splice(index, 1); + this.colorReferenceSchema.enumDescriptions.splice(index, 1); + } + this._onDidChangeSchema.fire(); + } + public getColors(): ColorContribution[] { return Object.keys(this.colorsById).map(id => this.colorsById[id]); } @@ -449,6 +476,13 @@ export const workbenchColorsSchemaId = 'vscode://schemas/workbench-colors'; let schemaRegistry = platform.Registry.as(JSONExtensions.JSONContribution); schemaRegistry.registerSchema(workbenchColorsSchemaId, colorRegistry.getColorSchema()); +const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(workbenchColorsSchemaId), 200); +colorRegistry.onDidChangeSchema(() => { + if (!delayer.isScheduled()) { + delayer.schedule(); + } +}); + // setTimeout(_ => console.log(colorRegistry.toString()), 5000); diff --git a/src/vs/workbench/services/themes/common/colorExtensionPoint.ts b/src/vs/workbench/services/themes/common/colorExtensionPoint.ts index e903d072150..b810fdfd67f 100644 --- a/src/vs/workbench/services/themes/common/colorExtensionPoint.ts +++ b/src/vs/workbench/services/themes/common/colorExtensionPoint.ts @@ -5,8 +5,9 @@ import * as nls from 'vs/nls'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { registerColor, getColorRegistry } from 'vs/platform/theme/common/colorRegistry'; +import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; import { Color } from 'vs/base/common/color'; +import { Registry } from 'vs/platform/registry/common/platform'; interface IColorExtensionPoint { id: string; @@ -14,11 +15,14 @@ interface IColorExtensionPoint { defaults: { light: string, dark: string, highContrast: string }; } -const colorReferenceSchema = getColorRegistry().getColorReferenceSchema(); +const colorRegistry: IColorRegistry = Registry.as(ColorRegistryExtensions.ColorContribution); + +const colorReferenceSchema = colorRegistry.getColorReferenceSchema(); const colorIdPattern = '^\\w+[.\\w+]*$'; const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'colors', + isDynamic: true, jsonSchema: { description: nls.localize('contributes.color', 'Contributes extension defined themable colors'), type: 'array', @@ -72,8 +76,8 @@ const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint { - for (const extension of extensions) { + configurationExtPoint.setHandler((extensions, delta) => { + for (const extension of delta.added) { const extensionValue = extension.value; const collector = extension.collector; @@ -93,30 +97,36 @@ export class ColorExtensionPoint { return Color.red; }; - extensionValue.forEach(extension => { - if (typeof extension.id !== 'string' || extension.id.length === 0) { + for (const colorContribution of extensionValue) { + if (typeof colorContribution.id !== 'string' || colorContribution.id.length === 0) { collector.error(nls.localize('invalid.id', "'configuration.colors.id' must be defined and can not be empty")); return; } - if (!extension.id.match(colorIdPattern)) { + if (!colorContribution.id.match(colorIdPattern)) { collector.error(nls.localize('invalid.id.format', "'configuration.colors.id' must follow the word[.word]*")); return; } - if (typeof extension.description !== 'string' || extension.id.length === 0) { + if (typeof colorContribution.description !== 'string' || colorContribution.id.length === 0) { collector.error(nls.localize('invalid.description', "'configuration.colors.description' must be defined and can not be empty")); return; } - let defaults = extension.defaults; + let defaults = colorContribution.defaults; if (!defaults || typeof defaults !== 'object' || typeof defaults.light !== 'string' || typeof defaults.dark !== 'string' || typeof defaults.highContrast !== 'string') { collector.error(nls.localize('invalid.defaults', "'configuration.colors.defaults' must be defined and must contain 'light', 'dark' and 'highContrast'")); return; } - registerColor(extension.id, { + colorRegistry.registerColor(colorContribution.id, { light: parseColorValue(defaults.light, 'configuration.colors.defaults.light'), dark: parseColorValue(defaults.dark, 'configuration.colors.defaults.dark'), hc: parseColorValue(defaults.highContrast, 'configuration.colors.defaults.highContrast') - }, extension.description); - }); + }, colorContribution.description); + } + } + for (const extension of delta.removed) { + const extensionValue = extension.value; + for (const colorContribution of extensionValue) { + colorRegistry.deregisterColor(colorContribution.id); + } } }); } From c798b53596ff5848110e9411fd9cd0fa254555e6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 23 Jan 2019 13:57:58 +0100 Subject: [PATCH 0518/1066] revert unintended change --- extensions/rust/package.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/rust/package.json b/extensions/rust/package.json index 58ee4e84987..4436da8bcaf 100644 --- a/extensions/rust/package.json +++ b/extensions/rust/package.json @@ -9,16 +9,16 @@ "update-grammar": "node ../../build/npm/update-grammar.js zargony/atom-language-rust grammars/rust.cson ./syntaxes/rust.tmLanguage.json" }, "contributes": { - "colors": [ - { - "id": "rub.addedResourceForeground", - "description": "%colors.added%", - "defaults": { - "light": "#587c0c", - "dark": "#81b88b", - "highContrast": "#1b5225" - } - } - ] + "languages": [{ + "id": "rust", + "extensions": [".rs"], + "aliases": ["Rust", "rust"], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "rust", + "path": "./syntaxes/rust.tmLanguage.json", + "scopeName":"source.rust" + }] } } \ No newline at end of file From ed5a04d3f73b33964d6a8bdf48ab2228e39c8662 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 23 Jan 2019 15:02:48 +0100 Subject: [PATCH 0519/1066] update to electron 3.1.0 (#66987) --- .yarnrc | 2 +- cgmanifest.json | 6 ++-- src/typings/electron.d.ts | 64 +++++++++++++++++++++++++++------------ test/smoke/package.json | 2 +- test/smoke/yarn.lock | 8 ++--- 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/.yarnrc b/.yarnrc index b32ae136d49..9e184a6f5ac 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "3.0.13" +target "3.1.0" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index dc9ec2f7fcc..89693a02d32 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -48,7 +48,7 @@ "git": { "name": "libchromiumcontent", "repositoryUrl": "https://github.com/electron/libchromiumcontent", - "commitHash": "e38e22ed9f48e6e395f2e9faa93d2d7411f52de4" + "commitHash": "29e02cd4c37777734f97d00b5a538d7c7acfa67a" } }, "isOnlyProductionDependency": true, @@ -73,12 +73,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "f26dcaeaf39e9b829aa0d4fb5a5cb59c5c314b33" + "commitHash": "4913fc81d1dab21f4b15c4cb682a218072447fed" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "3.0.13" + "version": "3.1.0" }, { "component": { diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index d7682bfa180..85ff31b320f 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 3.0.13 +// Type definitions for Electron 3.1.0 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -86,7 +86,7 @@ declare namespace Electron { webviewTag: WebviewTag; } - interface AllElectron extends MainInterface, RendererInterface {} + interface AllElectron extends MainInterface, RendererInterface { } const app: App; const autoUpdater: AutoUpdater; @@ -2179,20 +2179,9 @@ declare namespace Electron { * Start recording on all processes. Recording begins immediately locally and * asynchronously on child processes as soon as they receive the EnableRecording * request. The callback will be called once all child processes have acknowledged - * the startRecording request. categoryFilter is a filter to control what category - * groups should be traced. A filter can have an optional - prefix to exclude - * category groups that contain a matching category. Having both included and - * excluded category patterns in the same list is not supported. Examples: - * traceOptions controls what kind of tracing is enabled, it is a comma-delimited - * list. Possible options are: The first 3 options are trace recording modes and - * hence mutually exclusive. If more than one trace recording modes appear in the - * traceOptions string, the last one takes precedence. If none of the trace - * recording modes are specified, recording mode is record-until-full. The trace - * option will first be reset to the default option (record_mode set to - * record-until-full, enable_sampling and enable_systrace set to false) before - * options parsed from traceOptions are applied on it. + * the startRecording request. */ - startRecording(options: StartRecordingOptions, callback: Function): void; + startRecording(options: TraceCategoriesAndOptions | TraceConfig, callback: Function): void; /** * Stop monitoring on all processes. Once all child processes have acknowledged the * stopMonitoring request the callback is called. @@ -4102,6 +4091,7 @@ declare namespace Electron { */ setUserAgent(userAgent: string, acceptLanguages?: string): void; cookies: Cookies; + netLog: NetLog; protocol: Protocol; webRequest: WebRequest; } @@ -4496,6 +4486,42 @@ declare namespace Electron { static TouchBarSpacer: typeof TouchBarSpacer; } + interface TraceCategoriesAndOptions { + + // Docs: http://electron.atom.io/docs/api/structures/trace-categories-and-options + + /** + * – is a filter to control what category groups should be traced. A filter can + * have an optional prefix to exclude category groups that contain a matching + * category. Having both included and excluded category patterns in the same list + * is not supported. Examples: test_MyTest*, test_MyTest*,test_OtherStuff, + * -excluded_category1,-excluded_category2. + */ + categoryFilter: string; + /** + * Controls what kind of tracing is enabled, it is a comma-delimited sequence of + * the following strings: record-until-full, record-continuously, trace-to-console, + * enable-sampling, enable-systrace, e.g. 'record-until-full,enable-sampling'. The + * first 3 options are trace recording modes and hence mutually exclusive. If more + * than one trace recording modes appear in the traceOptions string, the last one + * takes precedence. If none of the trace recording modes are specified, recording + * mode is record-until-full. The trace option will first be reset to the default + * option (record_mode set to record-until-full, enable_sampling and + * enable_systrace set to false) before options parsed from traceOptions are + * applied on it. + */ + traceOptions: string; + } + + interface TraceConfig { + + // Docs: http://electron.atom.io/docs/api/structures/trace-config + + excluded_categories?: string[]; + included_categories?: string[]; + memory_dump_config?: MemoryDumpConfig; + } + interface Transaction { // Docs: http://electron.atom.io/docs/api/structures/transaction @@ -7807,6 +7833,9 @@ declare namespace Electron { args?: string[]; } + interface MemoryDumpConfig { + } + interface MenuItemConstructorOptions { /** * Will be called with click(menuItem, browserWindow, event) when the menu item is @@ -8546,11 +8575,6 @@ declare namespace Electron { traceOptions: string; } - interface StartRecordingOptions { - categoryFilter: string; - traceOptions: string; - } - interface SystemMemoryInfo { /** * The total amount of physical memory in Kilobytes available to the system. diff --git a/test/smoke/package.json b/test/smoke/package.json index 22a960e57d1..f3c439c99a8 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.0.13", + "electron": "3.1.0", "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 0c7e15ce308..a44a612cf72 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.0.13: - version "3.0.13" - resolved "https://registry.yarnpkg.com/electron/-/electron-3.0.13.tgz#7b065a3d130c6b6379dc78d49515e03f392c1303" - integrity sha512-tfx5jFgXhCmpe6oPjcesaRj7geHqQxrJdbpseanRzL9BbyYUtsj0HoxwPAUvCx4+52P6XryBwWTvne/1eBVf9Q== +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== dependencies: "@types/node" "^8.0.24" electron-download "^4.1.0" From 07b3b9e11f764aa20808f1e5c0b6bea225f3dd35 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 23 Jan 2019 16:01:30 +0100 Subject: [PATCH 0520/1066] Revert "debug: Breaking should jump to top frame that has source" This reverts commit b995df24fa0cf4c3e99463296601e76ae2e3ba73. --- src/vs/workbench/parts/debug/common/debug.ts | 2 +- src/vs/workbench/parts/debug/common/debugModel.ts | 10 +++++----- .../parts/debug/electron-browser/debugSession.ts | 10 ---------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/parts/debug/common/debug.ts b/src/vs/workbench/parts/debug/common/debug.ts index 28f659a2f7e..903ce32c4e4 100644 --- a/src/vs/workbench/parts/debug/common/debug.ts +++ b/src/vs/workbench/parts/debug/common/debug.ts @@ -393,7 +393,7 @@ export interface IDebugModel extends ITreeElement { getWatchExpressions(): ReadonlyArray; onDidChangeBreakpoints: Event; - onDidChangeCallStack: Event; + onDidChangeCallStack: Event; onDidChangeWatchExpressions: Event; } diff --git a/src/vs/workbench/parts/debug/common/debugModel.ts b/src/vs/workbench/parts/debug/common/debugModel.ts index e040e103468..428f37ebc6a 100644 --- a/src/vs/workbench/parts/debug/common/debugModel.ts +++ b/src/vs/workbench/parts/debug/common/debugModel.ts @@ -744,7 +744,7 @@ export class DebugModel implements IDebugModel { private schedulers = new Map(); private breakpointsSessionId: string; private readonly _onDidChangeBreakpoints: Emitter; - private readonly _onDidChangeCallStack: Emitter; + private readonly _onDidChangeCallStack: Emitter; private readonly _onDidChangeWatchExpressions: Emitter; constructor( @@ -758,7 +758,7 @@ export class DebugModel implements IDebugModel { this.sessions = []; this.toDispose = []; this._onDidChangeBreakpoints = new Emitter(); - this._onDidChangeCallStack = new Emitter(); + this._onDidChangeCallStack = new Emitter(); this._onDidChangeWatchExpressions = new Emitter(); } @@ -793,7 +793,7 @@ export class DebugModel implements IDebugModel { return this._onDidChangeBreakpoints.event; } - get onDidChangeCallStack(): Event { + get onDidChangeCallStack(): Event { return this._onDidChangeCallStack.event; } @@ -826,12 +826,12 @@ export class DebugModel implements IDebugModel { return thread.fetchCallStack(1).then(() => { if (!this.schedulers.has(thread.getId())) { this.schedulers.set(thread.getId(), new RunOnceScheduler(() => { - thread.fetchCallStack(19).then(() => this._onDidChangeCallStack.fire(thread)); + thread.fetchCallStack(19).then(() => this._onDidChangeCallStack.fire()); }, 420)); } this.schedulers.get(thread.getId()).schedule(); - this._onDidChangeCallStack.fire(thread); + this._onDidChangeCallStack.fire(); }); } diff --git a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts index 6da6f6a2f98..9f5c32a30c4 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugSession.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugSession.ts @@ -613,16 +613,6 @@ export class DebugSession implements IDebugSession { this.model.fetchCallStack(thread).then(() => { if (!event.body.preserveFocusHint && thread.getCallStack().length) { this.debugService.focusStackFrame(undefined, thread); - if (!this.debugService.getViewModel().focusedStackFrame) { - // There were no appropriate stack frames to focus. - // We need to listen on additional stack frame fetching and try to refocus #65012 - const listener = this.model.onDidChangeCallStack(t => { - if (t && t.getId() === thread.getId()) { - dispose(listener); - this.debugService.focusStackFrame(undefined, thread); - } - }); - } if (thread.stoppedDetails) { if (this.configurationService.getValue('debug').openDebug === 'openOnDebugBreak') { this.viewletService.openViewlet(VIEWLET_ID); From 845d977c8403e6ef6b23d669cd3a476fee24f6c8 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 23 Jan 2019 16:16:46 +0100 Subject: [PATCH 0521/1066] fixes #66972 --- src/vs/base/browser/ui/list/listView.ts | 27 +++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 153b169432a..701ab69ceb2 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -23,18 +23,6 @@ import { equals, distinct } from 'vs/base/common/arrays'; import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { disposableTimeout } from 'vs/base/common/async'; -function canUseTranslate3d(): boolean { - if (browser.isFirefox) { - return false; - } - - if (browser.getZoomLevel() !== 0) { - return false; - } - - return true; -} - interface IItem { readonly id: string; readonly element: T; @@ -172,6 +160,7 @@ export class ListView implements ISpliceable, IDisposable { private dragOverMouseY: number; private setRowLineHeight: boolean; private supportDynamicHeights: boolean; + private canUseTranslate3d: boolean | undefined = undefined; private dnd: IListViewDragAndDrop; private canDrop: boolean = false; @@ -434,14 +423,26 @@ export class ListView implements ISpliceable, IDisposable { } } - if (canUseTranslate3d() && !isWindows /* Windows: translate3d breaks subpixel-antialias (ClearType) unless a background is defined */) { + const canUseTranslate3d = !isWindows && !browser.isFirefox && browser.getZoomLevel() === 0; + + if (canUseTranslate3d) { const transform = `translate3d(0px, -${renderTop}px, 0px)`; this.rowsContainer.style.transform = transform; this.rowsContainer.style.webkitTransform = transform; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.top = '0'; + } } else { this.rowsContainer.style.top = `-${renderTop}px`; + + if (canUseTranslate3d !== this.canUseTranslate3d) { + this.rowsContainer.style.transform = ''; + this.rowsContainer.style.webkitTransform = ''; + } } + this.canUseTranslate3d = canUseTranslate3d; this.lastRenderTop = renderTop; this.lastRenderHeight = renderHeight; } From 16d3d13c5b92bc8630aa33064a7b814ac360380c Mon Sep 17 00:00:00 2001 From: alexet Date: Wed, 23 Jan 2019 15:46:23 +0000 Subject: [PATCH 0522/1066] 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 0523/1066] 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 0524/1066] - 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 0525/1066] 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 0526/1066] 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 0527/1066] 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 0528/1066] 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 0529/1066] 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 0530/1066] 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 0531/1066] 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 0532/1066] 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 0533/1066] 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 0534/1066] 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 0535/1066] 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 0536/1066] 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 0537/1066] 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 0538/1066] 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 0539/1066] 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 0540/1066] 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 0541/1066] 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 0542/1066] 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 0543/1066] 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 0544/1066] 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 0545/1066] 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 0546/1066] 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 0547/1066] 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 0548/1066] 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 0549/1066] 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 0550/1066] 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 0551/1066] 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 0552/1066] 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 0553/1066] 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 0554/1066] 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 0555/1066] 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 0556/1066] 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 0557/1066] :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 0558/1066] 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 0559/1066] 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 0560/1066] 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 0561/1066] 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 0562/1066] 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 0563/1066] 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 0564/1066] 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 0565/1066] 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 0566/1066] 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 0567/1066] 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 0568/1066] 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 0569/1066] 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 0570/1066] 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 0571/1066] 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 0572/1066] :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 0573/1066] 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 0574/1066] 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 0575/1066] 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 0576/1066] 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 0577/1066] 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 0578/1066] 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 0579/1066] 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 0580/1066] 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 0581/1066] 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 0582/1066] 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 0583/1066] 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 0584/1066] 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 0585/1066] 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 0586/1066] 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 0587/1066] 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 0588/1066] 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 0589/1066] 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 0590/1066] 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 0591/1066] 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 0592/1066] 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 0593/1066] 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 0594/1066] 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 0595/1066] 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 0596/1066] 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 0597/1066] 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 0598/1066] 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 0599/1066] 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 0600/1066] :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 0601/1066] 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 0602/1066] :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 0603/1066] 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 0604/1066] 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 0605/1066] 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 0606/1066] 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 0607/1066] 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 0608/1066] 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 0609/1066] 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 0610/1066] 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 0611/1066] 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 0612/1066] 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 0613/1066] 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 0614/1066] 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 0615/1066] 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 0616/1066] 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 0617/1066] 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 0618/1066] 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 0619/1066] 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 0620/1066] 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 0621/1066] 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 0622/1066] 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 0623/1066] 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 0624/1066] 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 0625/1066] 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 0626/1066] 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 0627/1066] 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 0628/1066] 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 0629/1066] 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 0630/1066] 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 0631/1066] 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 0632/1066] 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 0633/1066] 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 0634/1066] 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 0635/1066] 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 0636/1066] 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 0637/1066] 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 0638/1066] 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 0639/1066] 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 0640/1066] 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 0641/1066] 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 0642/1066] 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 0643/1066] 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 0644/1066] 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 0645/1066] 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 0646/1066] 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 0647/1066] 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 0648/1066] 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 0649/1066] 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 0650/1066] 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 0651/1066] 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 0652/1066] 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 0653/1066] 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 0654/1066] 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 0655/1066] 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 0656/1066] 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 0657/1066] 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 0658/1066] 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 0659/1066] 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 0660/1066] 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 0661/1066] 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 0662/1066] 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 0663/1066] 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 0664/1066] 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 0665/1066] 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 0666/1066] 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 0667/1066] 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 0668/1066] 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 0669/1066] 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 0670/1066] 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 0671/1066] 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 0672/1066] 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 0673/1066] 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 0674/1066] 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 0675/1066] 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 0676/1066] 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 0677/1066] 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 0678/1066] 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 b2933d647191d69c5ab2b7f3199752faabd6d878 Mon Sep 17 00:00:00 2001 From: Nilesh Date: Sun, 27 Jan 2019 22:25:09 +0530 Subject: [PATCH 0679/1066] Resolve vars in text for sendSequence to Terminal --- .../terminal/electron-browser/terminalActions.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index c77fbe34407..477670b053c 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -30,6 +30,9 @@ 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 { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { Schemas } from 'vs/base/common/network'; export const TERMINAL_PICKER_PREFIX = 'term '; @@ -285,7 +288,16 @@ export class SendSequenceTerminalCommand extends Command { if (!terminalInstance) { return; } - terminalInstance.sendText(args.text, false); + + const configurationResolverService = accessor.get(IConfigurationResolverService); + + const workspaceContextService = accessor.get(IWorkspaceContextService); + const historyService = accessor.get(IHistoryService); + const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; + + const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text); + terminalInstance.sendText(resolvedText, false); } } From 13b73269d92fde692268a8237e4eca3bae6fa70d Mon Sep 17 00:00:00 2001 From: Thien Do Date: Mon, 28 Jan 2019 00:57:32 +0700 Subject: [PATCH 0680/1066] 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 0681/1066] 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 0682/1066] 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 0683/1066] 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 0684/1066] 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 0685/1066] 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 0686/1066] 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 0687/1066] 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 0688/1066] 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 0689/1066] 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 0690/1066] 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 0691/1066] 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 0692/1066] 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 0693/1066] 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 0694/1066] #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 0695/1066] 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 0696/1066] 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 0697/1066] 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 0698/1066] 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 0699/1066] 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 0700/1066] 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 0701/1066] 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 0702/1066] 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 0703/1066] 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 0704/1066] 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 0705/1066] 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 0706/1066] 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 0707/1066] 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 0708/1066] 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 0709/1066] 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 0710/1066] 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 0711/1066] 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 0712/1066] 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 0713/1066] 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 0714/1066] 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 0715/1066] 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 0716/1066] [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 0717/1066] 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 0718/1066] 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 0719/1066] 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 0720/1066] 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 0721/1066] 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 0722/1066] 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 0723/1066] 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 0724/1066] 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 0725/1066] 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 0726/1066] 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 0727/1066] 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 0728/1066] 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 0729/1066] 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 0730/1066] 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 0731/1066] 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 0732/1066] 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 0733/1066] 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 0734/1066] 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 0735/1066] 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 0736/1066] 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 0737/1066] 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 0738/1066] 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 0739/1066] 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 0740/1066] 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 0741/1066] 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 0742/1066] 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 0743/1066] 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 0744/1066] 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 0745/1066] [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 0746/1066] 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 0747/1066] 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 0748/1066] 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 0749/1066] 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 0750/1066] 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 0751/1066] 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 0752/1066] 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 0753/1066] 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 0754/1066] 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 0755/1066] 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 0756/1066] 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 0757/1066] 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 0758/1066] 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 0759/1066] 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 0760/1066] 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 0761/1066] 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 0762/1066] 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 0763/1066] 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 0764/1066] 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 0765/1066] 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 0766/1066] 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 0767/1066] 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 0768/1066] 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 0769/1066] 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 0770/1066] 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 0771/1066] 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'; From 77b8c29d80d030cdb17861e8167eacfa36f8b241 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 29 Jan 2019 16:47:45 +0100 Subject: [PATCH 0772/1066] improve setting docs fixes #67407 --- src/vs/platform/update/node/update.config.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/update/node/update.config.contribution.ts b/src/vs/platform/update/node/update.config.contribution.ts index a3f7981e336..b68349fa06d 100644 --- a/src/vs/platform/update/node/update.config.contribution.ts +++ b/src/vs/platform/update/node/update.config.contribution.ts @@ -23,8 +23,8 @@ configurationRegistry.registerConfiguration({ tags: ['usesOnlineServices'], enumDescriptions: [ localize('none', "Disable updates."), - localize('manual', "Disable automatic update checks."), - localize('default', "Enable automatic update checks.") + localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."), + localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically.") ] }, 'update.enableWindowsBackgroundUpdates': { From 8247d14edbc0a58b31f0abf6fd97d9513ad0502a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 29 Jan 2019 16:51:45 +0100 Subject: [PATCH 0773/1066] fixes #67406 --- src/vs/base/browser/ui/list/listView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b599fe92406..078c3e50227 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -442,7 +442,7 @@ export class ListView implements ISpliceable, IDisposable { layout(height?: number, width?: number): void { let scrollDimensions: INewScrollDimensions = { - height: height || DOM.getContentHeight(this.domNode) + height: typeof height === 'number' ? height : DOM.getContentHeight(this.domNode) }; if (this.scrollableElementUpdateDisposable) { @@ -462,7 +462,7 @@ export class ListView implements ISpliceable, IDisposable { if (this.horizontalScrolling) { this.scrollableElement.setScrollDimensions({ - width: width || DOM.getContentWidth(this.domNode) + width: typeof width === 'number' ? width : DOM.getContentWidth(this.domNode) }); } } From 61e19a66f21d686289daae5a57e934cc66a2f1e6 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 29 Jan 2019 16:57:40 +0100 Subject: [PATCH 0774/1066] make jsonValidation dynamic (for #67362) --- .../services/jsonschemas/common/jsonValidationExtensionPoint.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts index 8e20f407866..487ca13beda 100644 --- a/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts +++ b/src/vs/workbench/services/jsonschemas/common/jsonValidationExtensionPoint.ts @@ -15,6 +15,7 @@ interface IJSONValidationExtensionPoint { const configurationExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'jsonValidation', + isDynamic: true, jsonSchema: { description: nls.localize('contributes.jsonValidation', 'Contributes json schema configuration.'), type: 'array', From 7ccd79898d004dd6195f493d736acf9408586c9a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Jan 2019 17:29:58 +0100 Subject: [PATCH 0775/1066] Add canAddExtension & canRemoveExtension methods to IExtensionService --- .../test/common/commandService.test.ts | 2 + .../services/extensions/common/extensions.ts | 12 ++++++ .../electron-browser/extensionService.ts | 38 +++++++++++++++++++ .../workbench/test/workbenchTestServices.ts | 2 + 4 files changed, 54 insertions(+) diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index e25c3f662ba..b686e562f1b 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -55,6 +55,8 @@ class SimpleExtensionService implements IExtensionService { } stopExtensionHost(): void { } + canAddExtension(): boolean { return false; } + canRemoveExtension(): boolean { return false; } } suite('CommandService', function () { diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 4057ccb4c56..4a7f1019f4a 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -181,6 +181,18 @@ export interface IExtensionService extends ICpuProfilerTarget { */ getExtension(id: string): Promise; + /** + * Returns `true` if the given extension can be added. Otherwise `false`. + * @param extension An extension + */ + canAddExtension(extension: IExtensionDescription): boolean; + + /** + * Returns `true` if the given extension can be removed. Otherwise `false`. + * @param extension An extension + */ + canRemoveExtension(extension: IExtensionDescription): boolean; + /** * Read all contributions to an extension point. */ diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index d61e59c3041..8d8af492103 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -275,6 +275,44 @@ export class ExtensionService extends Disposable implements IExtensionService { return true; } + public canAddExtension(extension: IExtensionDescription): boolean { + if (this._windowService.getConfiguration().remoteAuthority) { + return false; + } + + if (extension.extensionLocation.scheme !== Schemas.file) { + return false; + } + + const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + if (extensionDescription) { + // ignore adding an extension which is already running and cannot be removed + if (!this._canRemoveExtension(extensionDescription)) { + return false; + } + } + + return this._usesOnlyDynamicExtensionPoints(extension); + } + + public canRemoveExtension(extension: IExtensionDescription): boolean { + if (this._windowService.getConfiguration().remoteAuthority) { + return false; + } + + if (extension.extensionLocation.scheme !== Schemas.file) { + return false; + } + + const extensionDescription = this._registry.getExtensionDescription(extension.identifier); + if (!extensionDescription) { + // ignore removing an extension which is not running + return false; + } + + return this._canRemoveExtension(extensionDescription); + } + private _canRemoveExtension(extension: IExtensionDescription): boolean { if (this._extensionHostActiveExtensions.has(ExtensionIdentifier.toKey(extension.identifier))) { // Extension is running, cannot remove it safely diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 3de18af03d2..f902c08b46c 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -329,6 +329,8 @@ export class TestExtensionService implements IExtensionService { restartExtensionHost(): void { } startExtensionHost(): void { } stopExtensionHost(): void { } + canAddExtension(): boolean { return false; } + canRemoveExtension(): boolean { return false; } } export class TestMenuService implements IMenuService { From bcd8dbff5cf9f9949f045e5609e15261c337a090 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 29 Jan 2019 08:44:50 -0800 Subject: [PATCH 0776/1066] Fix ext host terminal --- .../api/electron-browser/mainThreadTerminalService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index bb267c2dbb4..4a846cf2952 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -12,6 +12,7 @@ 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 } = {}; @@ -22,6 +23,7 @@ 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 @@ -202,6 +204,11 @@ 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, From 5caaaf13c106b2eade5a98d91088bcce7d1992bf Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 08:59:49 -0800 Subject: [PATCH 0777/1066] Fix #67412 --- src/vs/workbench/parts/search/browser/media/searchview.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index 9538b2816c4..b0a438d8771 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -177,6 +177,9 @@ line-height: 22px; display: flex; overflow: hidden; +} + +.search-view .results.align-icons-and-twisties .linematch { padding-left: 22px; } From cdfaca550685dd60923247e2aadfe7023e45c716 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 09:59:07 -0800 Subject: [PATCH 0778/1066] Fix #67334 - disable multiselect on TOC tree --- src/vs/workbench/parts/preferences/browser/tocTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index 72acc8e1417..b3459279b3c 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -160,6 +160,7 @@ export class TOCTree extends ObjectTree { const filter = instantiationService.createInstance(SettingsTreeFilter, viewState); const options: IObjectTreeOptions = { filter, + multipleSelectionSupport: false, identityProvider: { getId(e) { return e.id; From bb96111b32e3304f29827e723c69582998fa07e5 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Tue, 29 Jan 2019 10:37:23 -0800 Subject: [PATCH 0779/1066] [HTML] Update language 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 e631b8e2db0..3c95ec49a4e 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.9", + "vscode-html-languageservice": "^2.1.11-next.10", "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 e0a65914a11..1cf165efbca 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.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== +vscode-html-languageservice@^2.1.11-next.10: + version "2.1.11-next.10" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.10.tgz#35c879e14fcf4d68870231eb5c46aa9a041b4aa1" + integrity sha512-HrQl0gIDLhTJB139nsMLhPkmG0YmweTgmd2MQlq7oki1+QFbIm3F/VyWpmGZR1aXGhRUmY58p1omg74NDy+4Lg== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 6f1cb1990953f58a67cccf0cf2f9b8e76e542835 Mon Sep 17 00:00:00 2001 From: Greg Van Liew Date: Tue, 29 Jan 2019 10:51:13 -0800 Subject: [PATCH 0780/1066] Fix jSDocCompletion deprecation message --- extensions/typescript-language-features/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index dc8b9dd2e34..b8161c9d79b 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -362,7 +362,7 @@ "type": "boolean", "default": true, "description": "%jsDocCompletion.enabled%", - "deprecationMessage": "jsDocCompletion.deprecationMessage", + "deprecationMessage": "%jsDocCompletion.deprecationMessage%", "scope": "resource" }, "javascript.implicitProjectConfig.checkJs": { From 1066572f4327dee7ad45a2c7b4c9601a2b38fc77 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Jan 2019 17:53:20 +0100 Subject: [PATCH 0781/1066] Fix #67389 --- .../parts/extensions/common/extensions.ts | 8 +- .../electron-browser/extensionsActions.ts | 109 +++++++++++------- .../node/extensionsWorkbenchService.ts | 48 ++++---- 3 files changed, 96 insertions(+), 69 deletions(-) diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 803a1e6a512..4684950b9a0 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -83,11 +83,11 @@ export interface IExtensionsWorkbenchService { queryLocal(): Promise; queryGallery(options?: IQueryOptions): Promise>; canInstall(extension: IExtension): boolean; - install(vsix: string): Promise; - install(extension: IExtension, promptToInstallDependencies?: boolean): Promise; + install(vsix: string): Promise; + install(extension: IExtension, promptToInstallDependencies?: boolean): Promise; uninstall(extension: IExtension): Promise; - installVersion(extension: IExtension, version: string): Promise; - reinstall(extension: IExtension): Promise; + installVersion(extension: IExtension, version: string): Promise; + reinstall(extension: IExtension): Promise; setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise; loadDependencies(extension: IExtension, token: CancellationToken): Promise; open(extension: IExtension, sideByside?: boolean): Promise; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 013105be402..04d14ae6cae 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, 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, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { ExtensionType, ExtensionIdentifier } 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'; @@ -56,6 +56,16 @@ import { IPartService } from 'vs/workbench/services/part/common/partService'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { coalesce } from 'vs/base/common/arrays'; +function toExtensionDescription(local: ILocalExtension): IExtensionDescription { + return { + identifier: new ExtensionIdentifier(local.identifier.id), + isBuiltin: local.type === ExtensionType.System, + isUnderDevelopment: false, + extensionLocation: local.location, + ...local.manifest + }; +} + const promptDownloadManually = (extension: IGalleryExtension | undefined, message: string, error: Error, instantiationService: IInstantiationService, notificationService: INotificationService, openerService: IOpenerService) => { if (!extension || error.name === INSTALL_ERROR_INCOMPATIBLE || error.name === INSTALL_ERROR_MALICIOUS) { return Promise.reject(error); @@ -171,9 +181,7 @@ export class InstallAction extends ExtensionAction { } private install(extension: IExtension): Promise { - return this.extensionsWorkbenchService.install(extension).then(() => { - alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName)); - }, err => { + return this.extensionsWorkbenchService.install(extension).then(null, err => { if (!extension.gallery) { return this.notificationService.error(err); } @@ -574,10 +582,9 @@ export class InstallAnotherVersionAction extends ExtensionAction { if (this.extension.version === pick.id) { return Promise.resolve(); } - return (pick.latest ? this.extensionsWorkbenchService.install(this.extension) : this.extensionsWorkbenchService.installVersion(this.extension, pick.id)) - .then(() => { - alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName)); - }, err => { + const promise: Promise = pick.latest ? this.extensionsWorkbenchService.install(this.extension) : this.extensionsWorkbenchService.installVersion(this.extension, pick.id); + return promise + .then(null, err => { if (!this.extension.gallery) { return this.notificationService.error(err); } @@ -1022,6 +1029,7 @@ export class ReloadAction extends ExtensionAction { this.enabled = true; if (!isEnabled) { this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension."); + alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName)); } else { this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); } @@ -1035,6 +1043,7 @@ export class ReloadAction extends ExtensionAction { // Requires reload to deactivate the extension this.enabled = true; this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension."); + alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName)); return; } } @@ -2228,7 +2237,9 @@ export class InstallVSIXAction extends Action { label = InstallVSIXAction.LABEL, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @INotificationService private readonly notificationService: INotificationService, - @IWindowService private readonly windowService: IWindowService + @IWindowService private readonly windowService: IWindowService, + @IExtensionService private readonly extensionService: IExtensionService, + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(id, label, 'extension-action install-vsix', true); } @@ -2244,17 +2255,25 @@ export class InstallVSIXAction extends Action { return Promise.resolve(null); } - return Promise.all(result.map(vsix => this.extensionsWorkbenchService.install(vsix))).then(() => { - this.notificationService.prompt( - Severity.Info, - localize('InstallVSIXAction.success', "Successfully installed the extension. Reload to enable it."), - [{ - label: localize('InstallVSIXAction.reloadNow', "Reload Now"), - run: () => this.windowService.reloadWindow() - }], - { sticky: true } - ); - }); + return Promise.all(result.map(vsix => this.extensionsWorkbenchService.install(vsix))) + .then(extensions => { + for (const extension of extensions) { + const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); + const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.identifier.id) + : localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id); + const actions = requireReload ? [{ + label: localize('InstallVSIXAction.reloadNow', "Reload Now"), + run: () => this.windowService.reloadWindow() + }] : []; + this.notificationService.prompt( + Severity.Info, + message, + actions, + { sticky: true } + ); + } + return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run(); + }); }); } } @@ -2270,7 +2289,8 @@ export class ReinstallAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @INotificationService private readonly notificationService: INotificationService, @IWindowService private readonly windowService: IWindowService, - @IViewletService private readonly viewletService: IViewletService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IExtensionService private readonly extensionService: IExtensionService ) { super(id, label); } @@ -2302,18 +2322,21 @@ export class ReinstallAction extends Action { } private reinstallExtension(extension: IExtension): Promise { - return this.viewletService.openViewlet(VIEWLET_ID) - .then((viewlet: IExtensionsViewlet) => { - viewlet.search(''); + return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run() + .then(() => { return this.extensionsWorkbenchService.reinstall(extension) - .then(() => { + .then(extension => { + const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); + const message = requireReload ? localize('ReinstallAction.successReload', "Please reload Visual Studio Code to complete reinstalling the extension {0}.", extension.identifier.id) + : localize('ReinstallAction.success', "Reinstalling the extension {0} is completed.", extension.identifier.id); + const actions = requireReload ? [{ + label: localize('InstallVSIXAction.reloadNow', "Reload Now"), + run: () => this.windowService.reloadWindow() + }] : []; this.notificationService.prompt( Severity.Info, - localize('ReinstallAction.success', "Successfully reinstalled the extension."), - [{ - label: localize('ReinstallAction.reloadNow', "Reload Now"), - run: () => this.windowService.reloadWindow() - }], + message, + actions, { sticky: true } ); }, error => this.notificationService.error(error)); @@ -2333,7 +2356,8 @@ export class InstallSpecificVersionOfExtensionAction extends Action { @IQuickInputService private readonly quickInputService: IQuickInputService, @INotificationService private readonly notificationService: INotificationService, @IWindowService private readonly windowService: IWindowService, - @IViewletService private readonly viewletService: IViewletService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IExtensionService private readonly extensionService: IExtensionService ) { super(id, label); } @@ -2383,18 +2407,21 @@ export class InstallSpecificVersionOfExtensionAction extends Action { } private install(extension: IExtension, version: string): Promise { - return this.viewletService.openViewlet(VIEWLET_ID) - .then((viewlet: IExtensionsViewlet) => { - viewlet.search(''); + return this.instantiationService.createInstance(ShowInstalledExtensionsAction, ShowInstalledExtensionsAction.ID, ShowInstalledExtensionsAction.LABEL).run() + .then(() => { return this.extensionsWorkbenchService.installVersion(extension, version) - .then(() => { + .then(extension => { + const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); + const message = requireReload ? localize('InstallAnotherVersionExtensionAction.successReload', "Please reload Visual Studio Code to complete installing the extension {0}.", extension.identifier.id) + : localize('InstallAnotherVersionExtensionAction.success', "Installing the extension {0} is completed.", extension.identifier.id); + const actions = requireReload ? [{ + label: localize('InstallAnotherVersionExtensionAction.reloadNow', "Reload Now"), + run: () => this.windowService.reloadWindow() + }] : []; this.notificationService.prompt( Severity.Info, - localize('Install Success', "Successfully installed the extension."), - [{ - label: localize('InstallAnotherVersionExtensionAction.reloadNow', "Reload Now"), - run: () => this.windowService.reloadWindow() - }], + message, + actions, { sticky: true } ); }, error => this.notificationService.error(error)); diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 167b592c380..6d25977a663 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -624,30 +624,30 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return !!(extension as Extension).gallery; } - install(extension: string | IExtension): Promise { + install(extension: string | IExtension): Promise { if (typeof extension === 'string') { - return this.installWithProgress(() => this.extensionService.install(URI.file(extension)).then(extensionIdentifier => this.checkAndEnableDisabledDependencies(extensionIdentifier))); - } - - if (!(extension instanceof Extension)) { - return Promise.resolve(undefined); + return this.installWithProgress(async () => { + const extensionIdentifier = await this.extensionService.install(URI.file(extension)); + this.checkAndEnableDisabledDependencies(extensionIdentifier); + return this.local.filter(local => areSameExtensions(local.identifier, extensionIdentifier))[0]; + }); } if (extension.isMalicious) { return Promise.reject(new Error(nls.localize('malicious', "This extension is reported to be problematic."))); } - const ext = extension as Extension; - const gallery = ext.gallery; + const gallery = extension.gallery; if (!gallery) { return Promise.reject(new Error('Missing gallery')); } - return this.installWithProgress( - () => this.extensionService.installFromGallery(gallery) - .then(() => this.checkAndEnableDisabledDependencies(gallery.identifier)) - , gallery.displayName); + return this.installWithProgress(async () => { + await this.extensionService.installFromGallery(gallery); + this.checkAndEnableDisabledDependencies(gallery.identifier); + return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; + }, gallery.displayName); } setEnablement(extensions: IExtension | IExtension[], enablementState: EnablementState): Promise { @@ -671,7 +671,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, }, () => this.extensionService.uninstall(toUninstall).then(() => undefined)); } - installVersion(extension: IExtension, version: string): Promise { + installVersion(extension: IExtension, version: string): Promise { if (!(extension instanceof Extension)) { return Promise.resolve(); } @@ -683,20 +683,20 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.galleryService.getCompatibleExtension(extension.gallery.identifier, version) .then(gallery => { if (!gallery) { - return undefined; + return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' with version '{1}' as it is not compatible with VS Code.", extension.gallery!.identifier.id, version))); + } + return this.installWithProgress(async () => { + await this.extensionService.installFromGallery(gallery); + if (extension.latestVersion !== version) { + this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version)); + } + return this.local.filter(local => areSameExtensions(local.identifier, gallery.identifier))[0]; } - return this.installWithProgress( - () => this.extensionService.installFromGallery(gallery) - .then(() => { - if (extension.latestVersion !== version) { - this.ignoreAutoUpdate(new ExtensionIdentifierWithVersion(gallery.identifier, version)); - } - }) , gallery.displayName); }); } - reinstall(extension: IExtension): Promise { + reinstall(extension: IExtension): Promise { 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; @@ -707,10 +707,10 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, return this.progressService.withProgress({ location: ProgressLocation.Extensions, source: `${toReinstall.identifier.id}` - }, () => this.extensionService.reinstallFromGallery(toReinstall).then(() => undefined)); + }, () => this.extensionService.reinstallFromGallery(toReinstall).then(() => this.local.filter(local => areSameExtensions(local.identifier, extension.identifier))[0])); } - private installWithProgress(installTask: () => Promise, extensionName?: string): Promise { + private installWithProgress(installTask: () => Promise, extensionName?: string): Promise { const title = extensionName ? nls.localize('installing named extension', "Installing '{0}' extension....", extensionName) : nls.localize('installing extension', 'Installing extension....'); return this.progressService.withProgress({ location: ProgressLocation.Extensions, From 2df07eba28887df77dc07eb2ac37676d9bbc1248 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Jan 2019 23:04:59 +0100 Subject: [PATCH 0782/1066] #66931 - Show feedback when action is completed - Verbose reload action --- .../electron-browser/extensionEditor.ts | 3 +- .../electron-browser/extensionsActions.ts | 101 ++++++++++++++++-- .../electron-browser/extensionsList.ts | 3 +- .../media/extensionActions.css | 12 ++- .../extensionsActions.test.ts | 4 +- 5 files changed, 107 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 763454e1e94..65574224a7d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -28,7 +28,7 @@ import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension 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'; +import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction } 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'; @@ -360,6 +360,7 @@ export class ExtensionEditor extends BaseEditor { const reloadAction = this.instantiationService.createInstance(ReloadAction); const actions = [ reloadAction, + this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), this.instantiationService.createInstance(EnableDropDownAction), this.instantiationService.createInstance(DisableDropDownAction, runningExtensions), diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 04d14ae6cae..69866708e18 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 { ThrottledDelayer } from 'vs/base/common/async'; +import { Throttler } 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'; @@ -964,7 +964,7 @@ export class ReloadAction extends ExtensionAction { private static readonly DisabledClass = `${ReloadAction.EnabledClass} disabled`; // Use delayer to wait for more updates - private throttler: ThrottledDelayer; + private throttler: Throttler; private disposables: IDisposable[] = []; constructor( @@ -974,13 +974,13 @@ export class ReloadAction extends ExtensionAction { @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService ) { super('extensions.reload', localize('reloadAction', "Reload"), ReloadAction.DisabledClass, false); - this.throttler = new ThrottledDelayer(50); + this.throttler = new Throttler(); this.extensionService.onDidChangeExtensions(this.update, this, this.disposables); this.update(); } update(): Promise { - return this.throttler.trigger(() => { + return this.throttler.queue(() => { this.enabled = false; this.tooltip = ''; if (!this.extension) { @@ -999,7 +999,6 @@ export class ReloadAction extends ExtensionAction { .then(runningExtensions => this.computeReloadState(runningExtensions, installed)); }).then(() => { this.class = this.enabled ? ReloadAction.EnabledClass : ReloadAction.DisabledClass; - this.label = localize('reloadAction', "Reload"); }); } @@ -1015,25 +1014,28 @@ export class ReloadAction extends ExtensionAction { if (isDifferentVersionRunning && !isDisabled) { // Requires reload to run the updated extension this.enabled = true; + this.label = localize('reloadToUpdateAction', "Reload to Update"); this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to complete the updating of this extension."); return; } if (isDisabled) { // Requires reload to disable the extension this.enabled = true; + this.label = localize('reloadToDisable', "Reload to Disable"); this.tooltip = localize('postDisableTooltip', "Please reload Visual Studio Code to complete the disabling of this extension."); return; } } else { - if (!isDisabled) { + if (!isDisabled && !(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) { this.enabled = true; - if (!isEnabled) { + if (isEnabled) { + this.label = localize('reloadToEnable', "Reload to Enable"); + this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); + } else { + this.label = localize('reloadToInstall', "Reload to Install"); this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension."); alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName)); - } else { - this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); } - return; } } return; @@ -1042,6 +1044,7 @@ export class ReloadAction extends ExtensionAction { if (isUninstalled && runningExtension) { // Requires reload to deactivate the extension this.enabled = true; + this.label = localize('reloadToUninstall', "Reload to Uninstall"); this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension."); alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName)); return; @@ -2045,6 +2048,84 @@ export class AddToWorkspaceRecommendationsAction extends AbstractConfigureRecomm } } +export class StatusLabelAction extends Action implements IExtensionContainer { + + private static readonly ENABLED_CLASS = 'extension-status-label'; + private static readonly DISABLED_CLASS = `${StatusLabelAction.ENABLED_CLASS} hide`; + + private status: ExtensionState | null = null; + private enablementState: EnablementState | null = null; + + private _extension: IExtension; + get extension(): IExtension { return this._extension; } + set extension(extension: IExtension) { + if (!(this._extension && areSameExtensions(this._extension.identifier, extension.identifier))) { + // Different extension. Reset + this.status = null; + this.enablementState = null; + } + this._extension = extension; + this.update(); + } + + constructor( + @IExtensionService private readonly extensionService: IExtensionService + ) { + super('extensions.action.statusLabel', '', StatusLabelAction.DISABLED_CLASS, false); + } + + update(): void { + this.computeLabel() + .then(label => { + this.label = label || ''; + this.class = label ? StatusLabelAction.ENABLED_CLASS : StatusLabelAction.DISABLED_CLASS; + }); + } + + private async computeLabel(): Promise { + if (!this.extension) { + return null; + } + + const currentStatus = this.status; + const currentEnablementState = this.enablementState; + this.status = this.extension.state; + this.enablementState = this.extension.enablementState; + + const runningExtensions = await this.extensionService.getExtensions(); + const canAddExtension = () => this.extension.local && (runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier)) || this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))); + const canRemoveExtension = () => this.extension.local && (runningExtensions.every(e => !areSameExtensions({ id: e.identifier.value }, this.extension.identifier)) || this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local))); + + if (currentStatus !== null) { + if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) { + return canAddExtension() ? localize('installed', "Installed") : null; + } + if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) { + return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null; + } + } + + if (currentEnablementState !== null) { + const currentlyEnabled = currentEnablementState === EnablementState.Enabled || currentEnablementState === EnablementState.WorkspaceEnabled; + const enabled = this.enablementState === EnablementState.Enabled || this.enablementState === EnablementState.WorkspaceEnabled; + if (!currentlyEnabled && enabled) { + return canAddExtension() ? localize('enabled', "Enabled") : null; + } + if (currentlyEnabled && !enabled) { + return canRemoveExtension() ? localize('disabled', "Disabled") : null; + } + + } + + return null; + } + + run(): Promise { + return Promise.resolve(null); + } + +} + export class MaliciousStatusLabelAction extends ExtensionAction { private static readonly Class = 'malicious-status'; diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts index fb69a556f6d..24c894eca8c 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsList.ts @@ -13,7 +13,7 @@ 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, MaliciousStatusLabelAction, ExtensionActionItem } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; +import { InstallAction, UpdateAction, ManageExtensionAction, ReloadAction, MaliciousStatusLabelAction, ExtensionActionItem, StatusLabelAction } from 'vs/workbench/parts/extensions/electron-browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget } from 'vs/workbench/parts/extensions/electron-browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -95,6 +95,7 @@ export class Renderer implements IPagedRenderer { this.instantiationService.createInstance(RatingsWidget, ratings, true) ]; const actions = [ + this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(UpdateAction), this.instantiationService.createInstance(ReloadAction), this.instantiationService.createInstance(InstallAction), diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index 2c976fa20b0..3f81cec95f9 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -34,26 +34,34 @@ .monaco-action-bar .action-item.disabled .action-label.extension-action.extension-editor-dropdown-action, .monaco-action-bar .action-item.disabled .action-label.extension-action.reload, .monaco-action-bar .action-item.disabled .action-label.disable-status.hide, +.monaco-action-bar .action-item.disabled .action-label.extension-status-label.hide, .monaco-action-bar .action-item.disabled .action-label.malicious-status.not-malicious { display: none; } .monaco-action-bar .action-item .action-label.disable-status, -.monaco-action-bar .action-item .action-label.malicious-status { +.monaco-action-bar .action-item .action-label.malicious-status, +.monaco-action-bar .action-item.disabled .action-label.extension-status-label { border-radius: 4px; color: inherit; background-color: transparent; opacity: 0.9; - font-style: italic; padding: 0 5px; line-height: initial; } +.monaco-action-bar .action-item .action-label.disable-status, +.monaco-action-bar .action-item .action-label.malicious-status { + font-style: italic; +} + +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label, .extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status, .extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status { font-weight: normal; } +.extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.extension-status-label:hover, .extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.disable-status:hover, .extension-editor>.header>.details>.actions>.monaco-action-bar .action-item .action-label.malicious-status:hover { opacity: 0.9; 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 281958141b7..3cebcc96d55 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 @@ -24,7 +24,7 @@ import { Emitter } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionService, IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { TestContextService, TestWindowService } from 'vs/workbench/test/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -84,7 +84,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); instantiationService.stubPromise(IExtensionManagementService, 'getExtensionsReport', []); instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage()); - instantiationService.stub(IExtensionService, { getExtensions: () => Promise.resolve([]), onDidChangeExtensions: new Emitter().event }); + instantiationService.stub(IExtensionService, { getExtensions: () => Promise.resolve([]), onDidChangeExtensions: new Emitter().event, canAddExtension: (extension: IExtensionDescription) => false, canRemoveExtension: (extension: IExtensionDescription) => false }); await (instantiationService.get(IExtensionEnablementService)).reset(); instantiationService.set(IExtensionsWorkbenchService, instantiationService.createInstance(ExtensionsWorkbenchService)); From be29fda7ee8a07f38535faf678a8b3f578bb2eba Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Jan 2019 23:24:02 +0100 Subject: [PATCH 0783/1066] Fix #67432 --- .../extensions/electron-browser/media/extensionsViewlet.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 648b9c762a8..9089f6ff17d 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -108,11 +108,11 @@ bottom: 5px; } -.extensions-viewlet.narrow > .extensions .extension > .icon { +.extensions-viewlet.narrow > .extensions .extension > .icon-container > .icon { display: none; } -.extensions-viewlet > .extensions .extension.loading > .icon { +.extensions-viewlet > .extensions .extension.loading > .icon-container > .icon { display: none; } From b2f7a7f9f8d39f494104af02f8f0aac556cab5f8 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 29 Jan 2019 14:28:53 -0800 Subject: [PATCH 0784/1066] Update filter icons --- .../browser/ui/list/media/filter-dark.svg | 54 ++----------------- .../browser/ui/list/media/no-filter-dark.svg | 54 ++----------------- 2 files changed, 8 insertions(+), 100 deletions(-) diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg index 67a29ec0eeb..b480653ee44 100644 --- a/src/vs/base/browser/ui/list/media/filter-dark.svg +++ b/src/vs/base/browser/ui/list/media/filter-dark.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg index 2189e00d559..6a272f99d83 100644 --- a/src/vs/base/browser/ui/list/media/no-filter-dark.svg +++ b/src/vs/base/browser/ui/list/media/no-filter-dark.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + From 35ecc489d91b77e1a04c6be1de8652903aec824f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Jan 2019 23:45:09 +0100 Subject: [PATCH 0785/1066] Fix #67270 --- src/vs/editor/contrib/hover/hover.ts | 2 +- src/vs/editor/contrib/hover/modesContentHover.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/hover/hover.ts b/src/vs/editor/contrib/hover/hover.ts index d729d098ccc..f6feacb8517 100644 --- a/src/vs/editor/contrib/hover/hover.ts +++ b/src/vs/editor/contrib/hover/hover.ts @@ -206,7 +206,7 @@ export class ModesHoverController implements IEditorContribution { private _createHoverWidget() { const renderer = new MarkdownRenderer(this._editor, this._modeService, this._openerService); - this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._markerDecorationsService, this._themeService); + this._contentWidget = new ModesContentHoverWidget(this._editor, renderer, this._markerDecorationsService, this._themeService, this._openerService); this._glyphWidget = new ModesGlyphHoverWidget(this._editor, renderer); } diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 94e66e196e4..f2d99c20e0d 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -27,6 +27,8 @@ import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays'; import { IMarker, IMarkerData } from 'vs/platform/markers/common/markers'; import { basename } from 'vs/base/common/paths'; import { IMarkerDecorationsService } from 'vs/editor/common/services/markersDecorationService'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { IOpenerService, NullOpenerService } from 'vs/platform/opener/common/opener'; const $ = dom.$; @@ -203,7 +205,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget { editor: ICodeEditor, markdownRenderer: MarkdownRenderer, markerDecorationsService: IMarkerDecorationsService, - private readonly _themeService: IThemeService + private readonly _themeService: IThemeService, + private readonly _openerService: IOpenerService | null = NullOpenerService, ) { super(ModesContentHoverWidget.ID, editor); @@ -485,8 +488,13 @@ export class ModesContentHoverWidget extends ContentHoverWidget { for (const { message, resource, startLineNumber, startColumn } of relatedInformation) { const item = dom.append(listElement, $('li')); const a = dom.append(item, $('a')); - a.setAttribute('data-href', `${resource.toString(false)}#${startLineNumber},${startColumn}`); a.innerText = `${basename(resource.path)}(${startLineNumber}, ${startColumn})`; + a.style.cursor = 'pointer'; + a.onclick = e => { + e.stopPropagation(); + e.preventDefault(); + this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` })).catch(onUnexpectedError); + }; const messageElement = dom.append(item, $('span')); messageElement.innerText = `: ${message}`; } From 330018c3cebb9512d821e550676310f9956f8960 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Jan 2019 00:04:12 +0100 Subject: [PATCH 0786/1066] fix null check --- src/vs/editor/contrib/hover/modesContentHover.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index f2d99c20e0d..af956cabdfb 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -493,7 +493,9 @@ export class ModesContentHoverWidget extends ContentHoverWidget { a.onclick = e => { e.stopPropagation(); e.preventDefault(); - this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` })).catch(onUnexpectedError); + if (this._openerService) { + this._openerService.open(resource.with({ fragment: `${startLineNumber},${startColumn}` })).catch(onUnexpectedError); + } }; const messageElement = dom.append(item, $('span')); messageElement.innerText = `: ${message}`; From 077a83ee6d2173170b17bfa11b31bfe2ca7cff01 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Tue, 29 Jan 2019 15:14:48 -0800 Subject: [PATCH 0787/1066] Update icons and colors --- .../browser/ui/list/media/filter-dark.svg | 6 +-- .../base/browser/ui/list/media/filter-hc.svg | 54 ++----------------- src/vs/base/browser/ui/list/media/filter.svg | 54 ++----------------- .../browser/ui/list/media/no-filter-dark.svg | 6 +-- .../browser/ui/list/media/no-filter-hc.svg | 54 ++----------------- .../base/browser/ui/list/media/no-filter.svg | 54 ++----------------- 6 files changed, 22 insertions(+), 206 deletions(-) diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg index b480653ee44..0925909a117 100644 --- a/src/vs/base/browser/ui/list/media/filter-dark.svg +++ b/src/vs/base/browser/ui/list/media/filter-dark.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg index b81a030f51b..0e2724f52e2 100644 --- a/src/vs/base/browser/ui/list/media/filter-hc.svg +++ b/src/vs/base/browser/ui/list/media/filter-hc.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + diff --git a/src/vs/base/browser/ui/list/media/filter.svg b/src/vs/base/browser/ui/list/media/filter.svg index 7d7d5f4fd35..f8c011064b1 100644 --- a/src/vs/base/browser/ui/list/media/filter.svg +++ b/src/vs/base/browser/ui/list/media/filter.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg index 6a272f99d83..5f495c006dc 100644 --- a/src/vs/base/browser/ui/list/media/no-filter-dark.svg +++ b/src/vs/base/browser/ui/list/media/no-filter-dark.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg index 173adcfa511..31d8f70f05a 100644 --- a/src/vs/base/browser/ui/list/media/no-filter-hc.svg +++ b/src/vs/base/browser/ui/list/media/no-filter-hc.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + diff --git a/src/vs/base/browser/ui/list/media/no-filter.svg b/src/vs/base/browser/ui/list/media/no-filter.svg index 55141722d66..760cc3c4403 100644 --- a/src/vs/base/browser/ui/list/media/no-filter.svg +++ b/src/vs/base/browser/ui/list/media/no-filter.svg @@ -1,51 +1,5 @@ - - - - - - image/svg+xml - - - - - - - - - - - + + + + From f5e8022d5aaa20e880a7e78a92a9e33561492f2b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 29 Jan 2019 17:25:39 -0800 Subject: [PATCH 0788/1066] Change from using `"apply": "preferred"` to using a new `"preferred": true` setting Fixes #67514 --- .../contrib/codeAction/codeActionCommands.ts | 46 +++++++++++++------ .../contrib/codeAction/codeActionTrigger.ts | 1 - 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index ee1e733a267..81ffd354aab 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -97,13 +97,6 @@ export class QuickFixController implements IEditorContribution { this._onApplyCodeAction(fixes[0]); return; } - - // Or if we have a single preferred action - const preferred = fixes.filter(fix => fix.isPreferred); - if (preferred.length === 1) { - this._onApplyCodeAction(preferred[0]); - return; - } } this._codeActionContextMenu.show(newState.actions, newState.position); @@ -217,11 +210,12 @@ export class QuickFixAction extends EditorAction { class CodeActionCommandArgs { public static fromUser(arg: any, defaults: { kind: CodeActionKind, apply: CodeActionAutoApply }): CodeActionCommandArgs { if (!arg || typeof arg !== 'object') { - return new CodeActionCommandArgs(defaults.kind, defaults.apply); + return new CodeActionCommandArgs(defaults.kind, defaults.apply, false); } return new CodeActionCommandArgs( CodeActionCommandArgs.getKindFromUser(arg, defaults.kind), - CodeActionCommandArgs.getApplyFromUser(arg, defaults.apply)); + CodeActionCommandArgs.getApplyFromUser(arg, defaults.apply), + CodeActionCommandArgs.getPreferredUser(arg)); } private static getApplyFromUser(arg: any, defaultAutoApply: CodeActionAutoApply) { @@ -229,7 +223,6 @@ class CodeActionCommandArgs { case 'first': return CodeActionAutoApply.First; case 'never': return CodeActionAutoApply.Never; case 'ifsingle': return CodeActionAutoApply.IfSingle; - case 'preferred': return CodeActionAutoApply.Preferred; default: return defaultAutoApply; } } @@ -240,9 +233,16 @@ class CodeActionCommandArgs { : defaultKind; } + private static getPreferredUser(arg: any): boolean { + return typeof arg.preferred === 'boolean' + ? arg.preferred + : false; + } + private constructor( public readonly kind: CodeActionKind, - public readonly apply: CodeActionAutoApply + public readonly apply: CodeActionAutoApply, + public readonly preferred: boolean, ) { } } @@ -262,7 +262,13 @@ export class CodeActionCommand extends EditorCommand { kind: CodeActionKind.Empty, apply: CodeActionAutoApply.IfSingle, }); - return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), { kind: args.kind, includeSourceActions: true }, args.apply); + return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), + { + kind: args.kind, + includeSourceActions: true, + onlyIncludePreferredActions: args.preferred, + }, + args.apply); } } @@ -302,7 +308,10 @@ export class RefactorAction extends EditorAction { }); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.refactor.noneMessage', "No refactorings available"), - { kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty }, + { + kind: CodeActionKind.Refactor.contains(args.kind) ? args.kind : CodeActionKind.Empty, + onlyIncludePreferredActions: args.preferred, + }, args.apply); } } @@ -335,7 +344,11 @@ export class SourceAction extends EditorAction { }); return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.source.noneMessage', "No source actions available"), - { kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty, includeSourceActions: true }, + { + kind: CodeActionKind.Source.contains(args.kind) ? args.kind : CodeActionKind.Empty, + includeSourceActions: true, + onlyIncludePreferredActions: args.preferred, + }, args.apply); } } @@ -391,7 +404,10 @@ export class AutoFixAction extends EditorAction { public run(_accessor: ServicesAccessor, editor: ICodeEditor): void { return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.autoFix.noneMessage', "No auto fixes available"), - { kind: CodeActionKind.QuickFix, onlyIncludePreferredActions: true }, + { + kind: CodeActionKind.QuickFix, + onlyIncludePreferredActions: true + }, CodeActionAutoApply.IfSingle); } } diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index 1332c015bd3..d782e836367 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -31,7 +31,6 @@ export class CodeActionKind { export const enum CodeActionAutoApply { IfSingle, First, - Preferred, Never, } From 8e7479e247fd296e4bed437949c20ec9fbcb56fd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 17:36:49 -0800 Subject: [PATCH 0789/1066] Fix missing outline on search result actions --- src/vs/workbench/parts/search/browser/searchView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 3260f6cee2e..029ed682d95 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -1730,6 +1730,6 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const outlineSelectionColor = theme.getColor(listActiveSelectionForeground); if (outlineSelectionColor) { - collector.addRule(`.monaco-workbench .search-view .monaco-tree.focused .monaco-tree-row.focused.selected:not(.highlighted) .action-label:focus { outline-color: ${outlineSelectionColor} }`); + collector.addRule(`.monaco-workbench .search-view .monaco-list.element-focused .monaco-list-row.focused.selected:not(.highlighted) .action-label:focus { outline-color: ${outlineSelectionColor} }`); } }); From 549afeee62ac49dfc56cc3e841794868af890f40 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 17:40:27 -0800 Subject: [PATCH 0790/1066] Fix #67500 - disable type to filter in search view --- .../parts/search/browser/media/searchview.css | 4 ---- .../parts/search/browser/searchResultsView.ts | 9 ++------- .../workbench/parts/search/browser/searchView.ts | 15 +-------------- 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/media/searchview.css b/src/vs/workbench/parts/search/browser/media/searchview.css index b0a438d8771..fd591c75b55 100644 --- a/src/vs/workbench/parts/search/browser/media/searchview.css +++ b/src/vs/workbench/parts/search/browser/media/searchview.css @@ -179,10 +179,6 @@ overflow: hidden; } -.search-view .results.align-icons-and-twisties .linematch { - padding-left: 22px; -} - .search-view .linematch > .match { overflow: hidden; text-overflow: ellipsis; diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 499495c2387..18a8d159b78 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -26,7 +26,6 @@ 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; @@ -163,7 +162,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, - matches: createMatches(node.filterData) - }); + templateData.label.setFile(fileMatch.resource(), { hideIcon: false }); 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 029ed682d95..a6ef7c274c9 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -62,8 +62,6 @@ 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.$; @@ -145,11 +143,10 @@ 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); @@ -618,7 +615,6 @@ 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) { @@ -636,15 +632,6 @@ 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 11338652050c7dd40a0100b36f8f1058d6910216 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 18:31:34 -0800 Subject: [PATCH 0791/1066] Fix #64974 - error in raw default settings json --- .../preferences/common/preferencesModels.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 498bbc64ddd..c738a41fc92 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -469,7 +469,7 @@ export class DefaultSettings extends Disposable { private initialize(): void { this._allSettingsGroups = this.parse(); - this._content = this.toContent(true, this._allSettingsGroups); + this._content = this.toContent(this._allSettingsGroups); } private parse(): ISettingsGroup[] { @@ -481,7 +481,7 @@ export class DefaultSettings extends Disposable { get raw(): string { if (!DefaultSettings._RAW) { - DefaultSettings._RAW = this.toContent(false, this.getRegisteredGroups()); + DefaultSettings._RAW = this.toContent(this.getRegisteredGroups()); } return DefaultSettings._RAW; @@ -673,18 +673,14 @@ export class DefaultSettings extends Disposable { return c1.order - c2.order; } - private toContent(asArray: boolean, settingsGroups: ISettingsGroup[]): string { + private toContent(settingsGroups: ISettingsGroup[]): string { const builder = new SettingsContentBuilder(); - if (asArray) { - builder.pushLine('['); - } + builder.pushLine('['); settingsGroups.forEach((settingsGroup, i) => { builder.pushGroup(settingsGroup); builder.pushLine(','); }); - if (asArray) { - builder.pushLine(']'); - } + builder.pushLine(']'); return builder.getContent(); } From 976e6bc828d4274f72dfc8e17c361ee91efbae71 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 19:53:35 -0800 Subject: [PATCH 0792/1066] Fix leak of suggest widget in settings editor - #67499 --- .../parts/codeEditor/electron-browser/suggestEnabledInput.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts index 3e2991208c4..4c4340a3db9 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts @@ -132,6 +132,7 @@ export class SuggestEnabledInput extends Widget implements IThemable { contributions: [SuggestController, SnippetController2, ContextMenuController, MenuPreventer, SelectionClipboard], isSimpleWidget: true, }); + this.disposables.push(this.inputWidget); let scopeHandle = uri.parse(resourceHandle); this.inputWidget.setModel(modelService.createModel('', null, scopeHandle, true)); From 603bf3837af6271aa609c645adab59e356953097 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 20:46:38 -0800 Subject: [PATCH 0793/1066] Clear lastFocusedSettingElement when focus moves to setting search --- .../parts/codeEditor/electron-browser/suggestEnabledInput.ts | 2 ++ .../parts/preferences/electron-browser/settingsEditor2.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts index 4c4340a3db9..8c692b4fcc2 100644 --- a/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts +++ b/src/vs/workbench/parts/codeEditor/electron-browser/suggestEnabledInput.ts @@ -196,6 +196,8 @@ export class SuggestEnabledInput extends Widget implements IThemable { })); } + public get onFocus(): Event { return this.inputWidget.onDidFocusEditorText; } + public setValue(val: string) { val = val.replace(/\s/g, ' '); const fullRange = new Range(1, 1, 1, this.getValue().length + 1); diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 9f52f2b6459..b177f2ce4f0 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -370,6 +370,10 @@ export class SettingsEditor2 extends BaseEditor { }) ); + this._register(this.searchWidget.onFocus(() => { + this.lastFocusedSettingElement = ''; + })); + this._register(attachSuggestEnabledInputBoxStyler(this.searchWidget, this.themeService, { inputBorder: settingsTextInputBorder })); From 2c987b20a4d65ee51c48b9e4eb8f50c926199468 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Jan 2019 21:05:28 -0800 Subject: [PATCH 0794/1066] Fix #67330 - settings tree styles overridden by other lists --- src/vs/workbench/parts/preferences/browser/settingsTree.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index ab40cc8e798..9f13b1ae66e 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -13,6 +13,7 @@ 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, ListAriaRootRole } from 'vs/base/browser/ui/list/list'; +import { DefaultStyleController } from 'vs/base/browser/ui/list/listWidget'; 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'; @@ -1306,6 +1307,7 @@ export class SettingsTree extends ObjectTree { return e.id; } }, + styleController: new DefaultStyleController(DOM.createStyleSheet(container), treeClass), filter: new SettingsTreeFilter(viewState) }); @@ -1372,7 +1374,9 @@ export class SettingsTree extends ObjectTree { listHoverOutline: editorBackground, listFocusOutline: editorBackground, listInactiveSelectionBackground: editorBackground, - listInactiveSelectionForeground: foreground + listInactiveSelectionForeground: foreground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground }, colors => { this.style(colors); })); From d5af2a20fc708b567f580144f97c1e946970400d Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 29 Jan 2019 22:37:41 -0800 Subject: [PATCH 0795/1066] fixes issue where hidden element orientation is not updated when hidden --- src/vs/base/browser/ui/grid/grid.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 5bbe5d9c30f..08a48e7b6f5 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -698,13 +698,19 @@ export class View implements IView { } layout(width: number, height: number, orientation: Orientation): void { - if (!this.visible) { + if (!this.visible && this.orientation === orientation) { return; } - this.view.layout(width, height, orientation); - this.width = width; - this.height = height; + + + if (this.visible) { + this.view.layout(width, height, orientation); + + this.width = width; + this.height = height; + } + this.orientation = orientation; } } \ No newline at end of file From 5a0091f4597720df2b5e674b2026cb1e0ecb5ebc Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 09:30:53 +0100 Subject: [PATCH 0796/1066] :lipstick: --- src/vs/base/browser/ui/grid/grid.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 08a48e7b6f5..2272913ff4a 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -698,19 +698,14 @@ export class View implements IView { } layout(width: number, height: number, orientation: Orientation): void { - if (!this.visible && this.orientation === orientation) { + this.orientation = orientation; + + if (!this.visible) { return; } - - - if (this.visible) { - this.view.layout(width, height, orientation); - - this.width = width; - this.height = height; - } - - this.orientation = orientation; + this.view.layout(width, height, orientation); + this.width = width; + this.height = height; } } \ No newline at end of file From be402f0320f3d1bee4fa04cb4868aa4a455a77af Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 09:41:58 +0100 Subject: [PATCH 0797/1066] grid branch nodes should not forward children's preferred sizes --- 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 9011a578e51..e4e6f8909ad 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -300,7 +300,7 @@ class BranchNode implements ISplitView, IDisposable { } private onDidChildrenChange(): void { - const onDidChildrenChange = Event.any(...this.children.map(c => c.onDidChange)); + const onDidChildrenChange = Event.map(Event.any(...this.children.map(c => c.onDidChange)), () => undefined); this.childrenChangeDisposable.dispose(); this.childrenChangeDisposable = onDidChildrenChange(this._onDidChange.fire, this._onDidChange); From 27aed2d87a2a94c2f78d4aa2343cabdfa62cb4d7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 09:56:59 +0100 Subject: [PATCH 0798/1066] fixes #67446 --- src/vs/base/common/color.ts | 16 ---------------- src/vs/platform/theme/common/colorRegistry.ts | 19 ++++--------------- src/vs/platform/theme/common/styler.ts | 9 +++++---- 3 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index fd8f70afd8d..f63e7aa86f1 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -399,22 +399,6 @@ export class Color { return new Color(new RGBA(r, g, b, a)); } - blend2(c: Color): Color { - const otherRgba = c.rgba; - - // Convert to 0..1 opacity - const thisA = this.rgba.a; - const otherA = otherRgba.a; - - const totalA = thisA + otherA; - - const r = this.rgba.r * thisA / totalA + otherRgba.r * otherA / totalA; - const g = this.rgba.g * thisA / totalA + otherRgba.g * otherA / totalA; - const b = this.rgba.b * thisA / totalA + otherRgba.b * otherA / totalA; - - return new Color(new RGBA(r, g, b, 1)); - } - flatten(...backgrounds: Color[]): Color { const background = backgrounds.reduceRight((accumulator, color) => { return Color._flatten(color, accumulator); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index aafd80bf811..b253d48e95e 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -235,6 +235,10 @@ export const listHighlightForeground = registerColor('list.highlightForeground', export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hc: '#B89500' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hc: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#4d9e4d', light: '#117711', hc: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); +export const listMatchesBackground = registerColor('list.matchesBackground', { light: '#efc1ad', dark: '#653723', hc: Color.black }, nls.localize('listMatchesBackground', 'Background color of the type filter widget in lists and trees.')); +export const listMatchesOutline = registerColor('list.matchesOutline', { dark: Color.transparent, light: Color.transparent, hc: '#f38518' }, nls.localize('listMatchesOutline', 'Outline color of the type filter widget in lists and trees.')); +export const listNoMatchesOutline = registerColor('list.noMatchesOutline', { dark: '#B89500', light: '#B89500', hc: contrastBorder }, nls.localize('listNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.')); +export const listMatchesShadow = registerColor('list.matchesShadow', { dark: '#000000', light: '#A8A8A8', hc: null }, nls.localize('listMatchesShadow', 'Shadow color of the type filter widget in lists and trees.')); export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#0066BF', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); @@ -435,21 +439,6 @@ function lessProminent(colorValue: ColorValue, backgroundColorValue: ColorValue, }; } -export function blend2(transparentColorValue: ColorValue, opaqueColorValue: ColorValue): ColorFunction { - return (theme) => { - let transparentColor = resolveColorValue(transparentColorValue, theme); - let opaqueColor = resolveColorValue(opaqueColorValue, theme); - if (transparentColor && opaqueColor) { - return opaqueColor.blend2(transparentColor); - } else if (transparentColor) { - return transparentColor; - } else if (opaqueColor) { - return opaqueColor; - } - return null; - }; -} - // ----- implementation /** diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index a1f3ee581d6..e96a4270c33 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, editorFindMatchHighlight, darken, editorWidgetBackground, blend2 } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listMatchesOutline, listNoMatchesOutline, listMatchesShadow, listMatchesBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -249,9 +249,10 @@ export const defaultListStyles: IColorMapping = { listFocusOutline: activeContrastBorder, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, - listMatchesBackground: blend2(editorFindMatchHighlight, editorWidgetBackground), - listNoMatchesOutline: inputValidationWarningBorder, - listMatchesShadow: widgetShadow + listMatchesBackground, + listMatchesOutline, + listNoMatchesOutline, + listMatchesShadow }; export interface IButtonStyleOverrides extends IStyleOverrides { From 4aa8c83b545f6a5c08fac367eb213c37fcce6039 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 11:40:15 +0100 Subject: [PATCH 0799/1066] Clear out windows, linux and osx fields to not have cycles inside the properties object fixes #67532 --- src/vs/workbench/parts/debug/node/debugger.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/parts/debug/node/debugger.ts b/src/vs/workbench/parts/debug/node/debugger.ts index 3c99a7123f1..7d0d8bff16d 100644 --- a/src/vs/workbench/parts/debug/node/debugger.ts +++ b/src/vs/workbench/parts/debug/node/debugger.ts @@ -289,6 +289,10 @@ export class Debugger implements IDebugger { description: nls.localize('debugPostDebugTask', "Task to run after debug session ends.") }; properties['internalConsoleOptions'] = INTERNAL_CONSOLE_OPTIONS_SCHEMA; + // Clear out windows, linux and osx fields to not have cycles inside the properties object + properties['windows'] = undefined; + properties['osx'] = undefined; + properties['linux'] = undefined; const osProperties = objects.deepClone(properties); properties['windows'] = { From 84424c84fd56cd0825e3e4bf9d786dd1011516f1 Mon Sep 17 00:00:00 2001 From: Thai Pangsakulyanont Date: Wed, 30 Jan 2019 17:46:45 +0700 Subject: [PATCH 0800/1066] Use jsonc parser to parse a config file This fixes a problem where the `typescript` VSCode task runs `tsc` with `-p` when it should run `-b` when `tsconfig.json` has the `"references"` property. ```js { "extends": "./tsconfig.app.json", // meow "references": [{ "path": "./tsconfig.lib.json" }] } ``` This bug happens because while `tsconfig.json` file allows comment, the parsing logic here uses vanilla `JSON.parse` which cannot parse comments. This commit fixes it by using `jsonc.parse` instead. --- extensions/typescript-language-features/src/features/task.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/task.ts b/extensions/typescript-language-features/src/features/task.ts index 550921e1488..1b1d4d3bded 100644 --- a/extensions/typescript-language-features/src/features/task.ts +++ b/extensions/typescript-language-features/src/features/task.ts @@ -7,6 +7,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +import * as jsonc from 'jsonc-parser'; import { ITypeScriptServiceClient } from '../typescriptService'; import { Lazy } from '../utils/lazy'; import { isImplicitProjectConfigFile } from '../utils/tsconfig'; @@ -217,7 +218,7 @@ class TscTaskProvider implements vscode.TaskProvider { } try { - const tsconfig = JSON.parse(result.toString()); + const tsconfig = jsonc.parse(result.toString()); if (tsconfig.references) { return resolve(['-b', project.path]); } From 9338d14634c2f2fb93728531759706f85dc4f86c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Jan 2019 12:03:44 +0100 Subject: [PATCH 0801/1066] Fix #66931 --- .../electron-browser/extensionsActions.ts | 49 ++++++++++++++----- .../media/extensionActions.css | 13 +++-- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 69866708e18..675cf034634 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -1012,16 +1012,18 @@ export class ReloadAction extends ExtensionAction { if (runningExtension) { const isDifferentVersionRunning = this.extension.version !== runningExtension.version; if (isDifferentVersionRunning && !isDisabled) { - // Requires reload to run the updated extension - this.enabled = true; - this.label = localize('reloadToUpdateAction', "Reload to Update"); - this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to complete the updating of this extension."); + if (!(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) { + // Requires reload to run the updated extension + this.enabled = true; + this.label = localize('reloadRequired', "Reload Required"); + this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to complete the updating of this extension."); + } return; } if (isDisabled) { // Requires reload to disable the extension this.enabled = true; - this.label = localize('reloadToDisable', "Reload to Disable"); + this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postDisableTooltip', "Please reload Visual Studio Code to complete the disabling of this extension."); return; } @@ -1029,10 +1031,10 @@ export class ReloadAction extends ExtensionAction { if (!isDisabled && !(this.extension.local && this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)))) { this.enabled = true; if (isEnabled) { - this.label = localize('reloadToEnable', "Reload to Enable"); + this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to complete the enabling of this extension."); } else { - this.label = localize('reloadToInstall', "Reload to Install"); + this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postInstallTooltip', "Please reload Visual Studio Code to complete the installation of this extension."); alert(localize('installExtensionComplete', "Installing extension {0} is completed. Please reload Visual Studio Code to enable it.", this.extension.displayName)); } @@ -1044,7 +1046,7 @@ export class ReloadAction extends ExtensionAction { if (isUninstalled && runningExtension) { // Requires reload to deactivate the extension this.enabled = true; - this.label = localize('reloadToUninstall', "Reload to Uninstall"); + this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postUninstallTooltip', "Please reload Visual Studio Code to complete the uninstallation of this extension."); alert(localize('uninstallExtensionComplete', "Please reload Visual Studio Code to complete the uninstallation of the extension {0}.", this.extension.displayName)); return; @@ -2055,6 +2057,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer { private status: ExtensionState | null = null; private enablementState: EnablementState | null = null; + private version: string | null = null; private _extension: IExtension; get extension(): IExtension { return this._extension; } @@ -2063,6 +2066,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer { // Different extension. Reset this.status = null; this.enablementState = null; + this.version = null; } this._extension = extension; this.update(); @@ -2089,16 +2093,35 @@ export class StatusLabelAction extends Action implements IExtensionContainer { const currentStatus = this.status; const currentEnablementState = this.enablementState; + const currentVersion = this.version; this.status = this.extension.state; this.enablementState = this.extension.enablementState; + this.version = this.extension.version; const runningExtensions = await this.extensionService.getExtensions(); - const canAddExtension = () => this.extension.local && (runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier)) || this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))); - const canRemoveExtension = () => this.extension.local && (runningExtensions.every(e => !areSameExtensions({ id: e.identifier.value }, this.extension.identifier)) || this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local))); + const canAddExtension = () => { + const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0]; + if (this.extension.local) { + if (runningExtension && this.extension.version === runningExtension.version) { + return true; + } + return this.extensionService.canAddExtension(toExtensionDescription(this.extension.local)); + } + return false; + }; + const canRemoveExtension = () => { + if (this.extension.local) { + if (runningExtensions.every(e => !areSameExtensions({ id: e.identifier.value }, this.extension.identifier))) { + return true; + } + return this.extensionService.canRemoveExtension(toExtensionDescription(this.extension.local)); + } + return false; + }; if (currentStatus !== null) { if (currentStatus === ExtensionState.Installing && this.status === ExtensionState.Installed) { - return canAddExtension() ? localize('installed', "Installed") : null; + return canAddExtension() ? currentVersion !== this.version ? localize('updated', "Updated") : localize('installed', "Installed") : null; } if (currentStatus === ExtensionState.Uninstalling && this.status === ExtensionState.Uninstalled) { return canRemoveExtension() ? localize('uninstalled', "Uninstalled") : null; @@ -2587,13 +2610,17 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground); if (extensionButtonProminentBackground) { collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-status-label { background-color: ${extensionButtonProminentBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-status-label { background-color: ${extensionButtonProminentBackgroundColor}; }`); } const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground); if (extensionButtonProminentForeground) { collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-status-label { color: ${extensionButtonProminentForegroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); + collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-status-label { color: ${extensionButtonProminentForegroundColor}; }`); } const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground); diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index 3f81cec95f9..fbfdba03550 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -39,19 +39,24 @@ display: none; } +.monaco-action-bar .action-item.disabled .action-label.extension-status-label:before { + content: '✓'; + padding-right: 4px; +} + .monaco-action-bar .action-item .action-label.disable-status, .monaco-action-bar .action-item .action-label.malicious-status, .monaco-action-bar .action-item.disabled .action-label.extension-status-label { - border-radius: 4px; - color: inherit; - background-color: transparent; opacity: 0.9; - padding: 0 5px; line-height: initial; + border-radius: 4px; + padding: 0 5px; } .monaco-action-bar .action-item .action-label.disable-status, .monaco-action-bar .action-item .action-label.malicious-status { + color: inherit; + background-color: transparent; font-style: italic; } From f6a0f99be8d4c666b5be2904aad883a522cb2e48 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Jan 2019 12:09:00 +0100 Subject: [PATCH 0802/1066] revert finally -> always --- .../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 ab6982abed3..eec60635669 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, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; +import { Limiter, always, 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'; @@ -306,7 +306,7 @@ export class ExtensionManagementService extends Disposable implements IExtension this.downloadInstallableExtension(extension, operation) .then(installableExtension => this.installExtension(installableExtension, ExtensionType.User, cancellationToken) - .then(local => pfs.rimraf(installableExtension.zipPath).finally(() => null).then(() => local))) + .then(local => always(pfs.rimraf(installableExtension.zipPath), () => null).then(() => local))) .then(local => this.installDependenciesAndPackExtensions(local, existingExtension) .then(() => local, error => this.uninstall(local, true).then(() => Promise.reject(error), () => Promise.reject(error)))) .then( @@ -464,7 +464,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 pfs.rimraf(extractPath).finally(() => null).then(() => Promise.reject(e)); + return always(pfs.rimraf(extractPath), () => null).then(() => Promise.reject(e)); })); } @@ -475,7 +475,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 => pfs.rimraf(extractPath).finally(() => null) + e => always(pfs.rimraf(extractPath), () => 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 6c2d081690a145ba06824526786e075292534ad4 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 12:14:18 +0100 Subject: [PATCH 0803/1066] fixes #67538 --- .../parts/files/electron-browser/views/explorerViewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e9f412f022b..ae2ded7dbc3 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -470,7 +470,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { if (!target) { // Droping onto the empty area. Do not accept if items dragged are already children of the root - if (items.every(i => i.parent.isRoot)) { + if (items.every(i => i.parent && i.parent.isRoot)) { return false; } From b1f4f266fa7b03bfc4c523eb69af2ce8beab955c Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 12:25:25 +0100 Subject: [PATCH 0804/1066] explorer: We did a top level refresh, reveal the active file fixes #67118 --- .../electron-browser/views/explorerView.ts | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 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 67b3aa76db8..ac741dd597f 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -193,13 +193,8 @@ export class ExplorerView extends ViewletPanel { // When the explorer viewer is loaded, listen to changes to the editor input this.disposables.push(this.editorService.onDidActiveEditorChange(() => { - if (this.autoReveal && !this.ignoreActiveEditorChange) { - const activeFile = this.getActiveFile(); - if (activeFile) { - this.explorerService.select(this.getActiveFile()); - } else { - this.tree.setSelection([]); - } + if (!this.ignoreActiveEditorChange) { + this.selectActiveFile(); } })); @@ -214,12 +209,7 @@ export class ExplorerView extends ViewletPanel { await this.setTreeInput(); } // Find resource to focus from active editor input if set - if (this.autoReveal) { - const activeFile = this.getActiveFile(); - if (activeFile) { - this.explorerService.select(activeFile, true); - } - } + this.selectActiveFile(true); } })); } @@ -243,6 +233,17 @@ export class ExplorerView extends ViewletPanel { this.tree.domFocus(); } + private selectActiveFile(reveal?: boolean): void { + if (this.autoReveal) { + const activeFile = this.getActiveFile(); + if (activeFile) { + this.explorerService.select(this.getActiveFile(), reveal); + } else { + this.tree.setSelection([]); + } + } + } + private createTree(container: HTMLElement): void { this.filter = this.instantiationService.createInstance(FilesFilter); this.disposables.push(this.filter); @@ -406,7 +407,12 @@ export class ExplorerView extends ViewletPanel { const recursive = !item; const toRefresh = item || this.tree.getInput(); - return this.tree.updateChildren(toRefresh, recursive); + return this.tree.updateChildren(toRefresh, recursive).then(() => { + if (!item) { + // We did a top level refresh, reveal the active file #67118 + this.selectActiveFile(true); + } + }); } getOptimalWidth(): number { From 6544b17b495645f2d6391c1b84d65f8d52e9d6b9 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 12:32:15 +0100 Subject: [PATCH 0805/1066] fix #67353 --- src/vs/editor/contrib/smartSelect/smartSelect.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 73878ae4990..cc62d7b0016 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -20,6 +20,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; class SelectionRanges { @@ -153,9 +154,9 @@ abstract class AbstractSmartSelect extends EditorAction { class GrowSelectionAction extends AbstractSmartSelect { constructor() { super(true, { - id: 'editor.action.smartSelect.grow', - label: nls.localize('smartSelect.grow', "Expand Select"), - alias: 'Expand Select', + id: 'editor.action.smartSelect.expand', + label: nls.localize('smartSelect.expand', "Expand Selection"), + alias: 'Expand Selection', precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, @@ -173,12 +174,15 @@ class GrowSelectionAction extends AbstractSmartSelect { } } +// renamed command id +CommandsRegistry.registerCommandAlias('editor.action.smartSelect.grow', 'editor.action.smartSelect.expand'); + class ShrinkSelectionAction extends AbstractSmartSelect { constructor() { super(false, { id: 'editor.action.smartSelect.shrink', - label: nls.localize('smartSelect.shrink', "Shrink Select"), - alias: 'Shrink Select', + label: nls.localize('smartSelect.shrink', "Shrink Selection"), + alias: 'Shrink Selection', precondition: null, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, From da79ab785b7487ecba564f1b3502c6f060acc440 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 12:36:29 +0100 Subject: [PATCH 0806/1066] fix #67478 --- .../browser/parts/editor/media/notabstitlecontrol.css | 3 +-- 1 file changed, 1 insertion(+), 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 4c96ebbc1f1..066987b790b 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -61,8 +61,7 @@ .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, -.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .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; } From 19d2f2195014ba46a1cfa26b76fdb7820391c9f5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 12:42:03 +0100 Subject: [PATCH 0807/1066] enforce that uri have scheme, #67373 --- src/vs/workbench/api/node/extHostWindow.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostWindow.ts b/src/vs/workbench/api/node/extHostWindow.ts index 244dd5675f5..87256bdd78e 100644 --- a/src/vs/workbench/api/node/extHostWindow.ts +++ b/src/vs/workbench/api/node/extHostWindow.ts @@ -8,6 +8,7 @@ import { ExtHostWindowShape, MainContext, MainThreadWindowShape, IMainContext } import { WindowState } from 'vscode'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { isFalsyOrWhitespace } from 'vs/base/common/strings'; export class ExtHostWindow implements ExtHostWindowShape { @@ -38,7 +39,9 @@ export class ExtHostWindow implements ExtHostWindowShape { } openUri(uri: URI): Promise { - if (uri.scheme === Schemas.command) { + if (isFalsyOrWhitespace(uri.scheme)) { + return Promise.reject('Invalid scheme - cannot be empty'); + } else if (uri.scheme === Schemas.command) { return Promise.reject(`Invalid scheme '${uri.scheme}'`); } return this._proxy.$openUri(uri); From 6a914669c1fc8db7eac3c0dbcc845dc37c848419 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 12:51:33 +0100 Subject: [PATCH 0808/1066] increase opacity to 0.5 for cut items fixes #67436 --- .../parts/files/electron-browser/media/explorerviewlet.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 746324d6769..e993c16c5ff 100644 --- a/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css +++ b/src/vs/workbench/parts/files/electron-browser/media/explorerviewlet.css @@ -48,7 +48,7 @@ } .explorer-viewlet .explorer-item.cut { - opacity: 0.3; + opacity: 0.5; } .explorer-viewlet .explorer-item.explorer-item-edited .label-name { From 707ac5ab2a8a578bd8a5122db950091884d625ed Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 14:22:23 +0100 Subject: [PATCH 0809/1066] fixes #67348 fixes #67345 --- .../parts/files/electron-browser/fileActions.ts | 11 ++++++----- .../files/electron-browser/views/explorerViewer.ts | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 3c64e250710..0b22d39d37d 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -45,6 +45,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { ExplorerItem } from 'vs/workbench/parts/files/common/explorerModel'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { sequence } from 'vs/base/common/async'; export const NEW_FILE_COMMAND_ID = 'explorer.newFile'; export const NEW_FILE_LABEL = nls.localize('newFile', "New File"); @@ -469,7 +470,7 @@ class PasteFileAction extends BaseErrorReportingAction { target = this.element.isDirectory ? this.element : this.element.parent; } - const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory }); + const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwirte: pasteShouldMove }); // Copy File const promise = pasteShouldMove ? this.fileService.moveFile(fileToPaste, targetFile) : this.fileService.copyFile(fileToPaste, targetFile); @@ -490,11 +491,11 @@ class PasteFileAction extends BaseErrorReportingAction { } } -export function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean }): URI { +export function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean, allowOverwirte: boolean }): URI { let name = resources.basenameOrAuthority(fileToPaste.resource); let candidate = resources.joinPath(targetFolder.resource, name); - while (true) { + while (true && !fileToPaste.allowOverwirte) { if (!targetFolder.root.find(candidate)) { break; } @@ -1126,8 +1127,8 @@ export const pasteFileHandler = (accessor: ServicesAccessor) => { const clipboardService = accessor.get(IClipboardService); const explorerContext = getContext(listService.lastFocusedList); - return Promise.all(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => { + return sequence(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => { const pasteFileAction = instantationService.createInstance(PasteFileAction, explorerContext.stat); - return pasteFileAction.run(toCopy); + return () => pasteFileAction.run(toCopy); })); }; 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 ae2ded7dbc3..cc0e63da297 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -769,7 +769,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Reuse duplicate action if user copies if (isCopy) { - return this.fileService.copyFile(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory })).then(stat => { + return this.fileService.copyFile(source.resource, findValidPasteFileTarget(target, { resource: source.resource, isDirectory: source.isDirectory, allowOverwirte: false })).then(stat => { if (!stat.isDirectory) { return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }).then(() => undefined); } From c1ae93ef0acef242b04618227b217519142430a3 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 14:45:58 +0100 Subject: [PATCH 0810/1066] Tree: ctrl/shift+click should not expand fixes #67539 --- src/vs/base/browser/ui/tree/abstractTree.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 921d5101b44..e4a366569f7 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter } from 'vs/base/common/event'; @@ -915,6 +915,9 @@ export abstract class AbstractTree implements IDisposable if (!node) { return; } + if (isSelectionRangeChangeEvent(e) || isSelectionSingleChangeEvent(e)) { + return; + } const location = this.model.getNodeLocation(node); const recursive = e.browserEvent.altKey; From 4d57e22acdeddb1e3650f15aa0a429150b9b325a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 14:51:38 +0100 Subject: [PATCH 0811/1066] fix #67504 --- src/vs/platform/actions/common/actions.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 6632fd1841a..6c8df2fabcd 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -133,15 +133,20 @@ export interface ICommandsMap { export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { private readonly _commands: { [id: string]: ICommandAction } = Object.create(null); - private readonly _menuItems: { [loc: string]: Array } = Object.create(null); + private readonly _menuItems: { [loc: number]: Array } = Object.create(null); private readonly _onDidChangeMenu = new Emitter(); readonly onDidChangeMenu: Event = this._onDidChangeMenu.event; addCommand(command: ICommandAction): IDisposable { this._commands[command.id] = command; + this._onDidChangeMenu.fire(MenuId.CommandPalette); return { - dispose: () => delete this._commands[command.id] + dispose: () => { + if (delete this._commands[command.id]) { + this._onDidChangeMenu.fire(MenuId.CommandPalette); + } + } }; } @@ -177,7 +182,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { } getMenuItems(id: MenuId): Array { - const result = this._menuItems[id] || []; + const result = this._menuItems[id].slice(0) || []; if (id === MenuId.CommandPalette) { // CommandPalette is special because it shows From fd4f54ee3683a91b3babc7a67b82d79c5b23f509 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 15:04:03 +0100 Subject: [PATCH 0812/1066] Revert "fixes #67041" This reverts commit 389e4283c0b3839aa3717346d745cd85cfd8efa3. --- .../electron-browser/views/openEditorsView.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 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 06474606ccd..398894eee0a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -146,11 +146,8 @@ export class OpenEditorsView extends ViewletPanel { break; } case GroupChangeKind.EDITOR_OPEN: { - setTimeout(() => { - this.list.splice(index, 0, [new OpenEditor(e.editor, group)]); - this.updateSize(); - this.focusActiveEditor(); - }, this.structuralRefreshDelay); + this.list.splice(index, 0, [new OpenEditor(e.editor, group)]); + setTimeout(() => this.updateSize(), this.structuralRefreshDelay); break; } case GroupChangeKind.EDITOR_CLOSE: { @@ -398,11 +395,9 @@ 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); - if (index < this.list.length) { - this.list.setFocus([index]); - this.list.setSelection([index]); - this.list.reveal(index); - } + this.list.setFocus([index]); + this.list.setSelection([index]); + this.list.reveal(index); } else { this.list.setFocus([]); this.list.setSelection([]); From f62961367f3e8125fa38189e381007fa4f572a20 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Jan 2019 15:04:58 +0100 Subject: [PATCH 0813/1066] fix #66678 --- src/vs/base/browser/ui/tree/abstractTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e4a366569f7..baf6d4ba919 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -691,6 +691,7 @@ export abstract class AbstractTree implements IDisposable this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; + this.view.onTap(this.reactOnMouseClick, this, this.disposables); this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); if (_options.keyboardSupport !== false) { From 6d2dd05f91cc23ae05e5e26dc5b0a9eee4e2da4c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 14:35:02 +0100 Subject: [PATCH 0814/1066] missing dispose call in explorer viewer --- .../parts/files/electron-browser/views/explorerViewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cc0e63da297..fe5b0957b70 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -247,7 +247,7 @@ export class FilesRenderer implements ITreeRenderer, index: number, templateData: IFileTemplateData): void { - // noop + templateData.elementDisposable.dispose(); } disposeTemplate(templateData: IFileTemplateData): void { From b1d8e8dca9116f60e32e60539aba615f207eb129 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 15:13:17 +0100 Subject: [PATCH 0815/1066] refactor async data tree to render less fixes #66659 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 275 ++++++++++--------- src/vs/base/browser/ui/tree/media/tree.css | 1 + 2 files changed, 153 insertions(+), 123 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c4ea62fed4a..bfcb32022e6 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -11,18 +11,16 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { timeout, always, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { toggleClass } from 'vs/base/browser/dom'; import { Iterator } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { toggleClass } from 'vs/base/browser/dom'; -enum AsyncDataTreeNodeState { - Uninitialized, - Loaded, - Loading, - Slow, - Disposed +const enum AsyncDataTreeNodeState { + Uninitialized = 'uninitialized', + Loaded = 'loaded', + Loading = 'loading' } interface IAsyncDataTreeNode { @@ -31,6 +29,10 @@ interface IAsyncDataTreeNode { readonly children: IAsyncDataTreeNode[]; readonly id?: string | null; state: AsyncDataTreeNodeState; + hasChildren: boolean; + needsRefresh: boolean; + slow: boolean; + disposed: boolean; } function isAncestor(ancestor: IAsyncDataTreeNode, descendant: IAsyncDataTreeNode): boolean { @@ -88,7 +90,7 @@ class DataTreeRenderer implements ITreeRe } renderTwistie(element: IAsyncDataTreeNode, twistieElement: HTMLElement): boolean { - toggleClass(twistieElement, 'loading', element.state === AsyncDataTreeNodeState.Slow); + toggleClass(twistieElement, 'loading', element.slow); return false; } @@ -221,10 +223,18 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt }; } -function asTreeElement(node: IAsyncDataTreeNode): ITreeElement> { +function asTreeElement(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): ITreeElement> { + let collapsed: boolean | undefined; + + if (viewStateContext && node.id) { + collapsed = viewStateContext.viewState.expanded.indexOf(node.id) === -1; + } + return { element: node, - children: Iterator.map(Iterator.fromArray(node.children), asTreeElement) + children: Iterator.map(Iterator.fromArray(node.children), child => asTreeElement(child, viewStateContext)), + collapsible: node.hasChildren, + collapsed }; } @@ -252,14 +262,16 @@ export class AsyncDataTree implements IDisposable private readonly tree: ObjectTree, TFilterData>; private readonly root: IAsyncDataTreeNode; - private readonly nodes = new Map>(); - private readonly currentRefreshCalls = new Map, Promise>(); + private readonly renderedNodes = new Map>(); + private readonly subTreeRefreshPromises = new Map, Promise>(); private readonly refreshPromises = new Map, CancelablePromise>(); + private readonly identityProvider?: IIdentityProvider; private readonly autoExpandSingleChildren: boolean; - private readonly _onDidChangeNodeState = new Emitter>(); + private readonly _onDidRender = new Emitter(); + private readonly _onDidChangeNodeSlowState = new Emitter>(); protected readonly disposables: IDisposable[] = []; @@ -288,7 +300,7 @@ export class AsyncDataTree implements IDisposable this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; const objectTreeDelegate = new ComposedTreeDelegate>(delegate); - const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeState.event)); + const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); const objectTreeOptions = asObjectTreeOptions(options) || {}; this.tree = new ObjectTree(container, objectTreeDelegate, objectTreeRenderers, objectTreeOptions); @@ -298,6 +310,10 @@ export class AsyncDataTree implements IDisposable parent: null, children: [], state: AsyncDataTreeNodeState.Uninitialized, + hasChildren: true, + needsRefresh: false, + disposed: false, + slow: false }; if (this.identityProvider) { @@ -307,7 +323,7 @@ export class AsyncDataTree implements IDisposable }; } - this.nodes.set(null, this.root); + this.renderedNodes.set(null, this.root); this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); } @@ -380,12 +396,12 @@ export class AsyncDataTree implements IDisposable } } - updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + async updateChildren(element: TInput | T = this.root.element, recursive = true, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { if (typeof this.root.element === 'undefined') { throw new Error('Tree input not set'); } - return this.refreshNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); + await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } // View @@ -423,7 +439,8 @@ export class AsyncDataTree implements IDisposable const result = this.tree.expand(node === this.root ? null : node, recursive); if (node.state === AsyncDataTreeNodeState.Loading) { - await this.currentRefreshCalls.get(node)!; + await this.subTreeRefreshPromises.get(node)!; + await Event.toPromise(this._onDidRender.event); } return result; @@ -532,7 +549,7 @@ export class AsyncDataTree implements IDisposable // Implementation private getDataNode(element: TInput | T): IAsyncDataTreeNode { - const node: IAsyncDataTreeNode | undefined = this.nodes.get((element === this.root.element ? null : element) as T); + const node: IAsyncDataTreeNode | undefined = this.renderedNodes.get((element === this.root.element ? null : element) as T); if (!node) { throw new Error(`Data tree node not found: ${element}`); @@ -541,25 +558,31 @@ export class AsyncDataTree implements IDisposable return node; } - private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { - await this.queueRefresh(node, recursive, reason, viewStateContext); + private async refreshAndRenderNode(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + await this.refreshNode(node, recursive, viewStateContext); + this.render(node, viewStateContext); - if (recursive) { - await Promise.all(node.children.map(child => this.refreshNode(child, recursive, reason, viewStateContext))); + if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { + const treeNode = this.tree.getNode(node); + const visibleChildren = treeNode.children.filter(node => node.visible); + + if (visibleChildren.length === 1) { + await this.tree.expand(visibleChildren[0].element, false); + } } } - private async queueRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { - if (node.state === AsyncDataTreeNodeState.Disposed) { + private async refreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + if (node.disposed) { console.error('Async data tree node is disposed'); return; } let result: Promise | undefined; - this.currentRefreshCalls.forEach((refreshPromise, refreshNode) => { + this.subTreeRefreshPromises.forEach((refreshPromise, refreshNode) => { if (!result && intersects(refreshNode, node)) { - result = refreshPromise.then(() => this.queueRefresh(node, recursive, reason, viewStateContext)); + result = refreshPromise.then(() => this.refreshNode(node, recursive, viewStateContext)); } }); @@ -567,65 +590,85 @@ export class AsyncDataTree implements IDisposable return result; } - result = this.doRefresh(node, recursive, reason, viewStateContext); - - this.currentRefreshCalls.set(node, result); - return always(result, () => this.currentRefreshCalls.delete(node)); - } - - private async doRefresh(node: IAsyncDataTreeNode, recursive: boolean, reason: ChildrenResolutionReason, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { - const hasChildren = !!this.dataSource.hasChildren(node.element!); - - if (!hasChildren) { - this.setChildren(node, [], recursive, viewStateContext); - return; - } - - if (node !== this.root && (!this.tree.isCollapsible(node) || this.tree.isCollapsed(node))) { - node.state = AsyncDataTreeNodeState.Uninitialized; - return; - } - - node.state = AsyncDataTreeNodeState.Loading; - this._onDidChangeNodeState.fire(node); - - const slowTimeout = timeout(800); - - slowTimeout.then(() => { - node.state = AsyncDataTreeNodeState.Slow; - this._onDidChangeNodeState.fire(node); - }, _ => null); + result = this.doRefreshSubTree(node, recursive, viewStateContext); + this.subTreeRefreshPromises.set(node, result); try { - const children = await this.doGetChildren(node); - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Loaded; - this._onDidChangeNodeState.fire(node); + await result; + } finally { + this.subTreeRefreshPromises.delete(node); + } + } - this.setChildren(node, children, recursive, viewStateContext); + private async doRefreshSubTree(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + node.state = AsyncDataTreeNodeState.Loading; - if (node !== this.root && this.autoExpandSingleChildren && reason === ChildrenResolutionReason.Expand) { - const treeNode = this.tree.getNode(node); - const visibleChildren = treeNode.children.filter(node => node.visible); + try { + await this.doRefreshNode(node, recursive, viewStateContext); - if (visibleChildren.length === 1) { - this.tree.expand(visibleChildren[0].element, false); - } + if (recursive) { + const childrenToRefresh = node.children + .filter(child => { + if (child.needsRefresh) { + child.needsRefresh = false; + return true; + } + + // TODO@joao: is this still needed? + if (child.hasChildren && child.state === AsyncDataTreeNodeState.Loaded) { + return true; + } + + if (!viewStateContext || !child.id) { + return false; + } + + return viewStateContext.viewState.expanded.indexOf(child.id) > -1; + }); + + await Promise.all(childrenToRefresh.map(child => this.doRefreshSubTree(child, recursive, viewStateContext))); } + } finally { + node.state = AsyncDataTreeNodeState.Loaded; + } + } + + private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise { + node.hasChildren = !!this.dataSource.hasChildren(node.element!); + + let childrenPromise: Promise; + + if (!node.hasChildren) { + childrenPromise = Promise.resolve([]); + } else { + const slowTimeout = timeout(800); + + slowTimeout.then(() => { + node.slow = true; + this._onDidChangeNodeSlowState.fire(node); + }, _ => null); + + childrenPromise = always(this.doGetChildren(node), () => slowTimeout.cancel()); + } + + try { + const children = await childrenPromise; + this.setChildren(node, children, recursive, viewStateContext); } catch (err) { if (isPromiseCanceledError(err)) { return; } - slowTimeout.cancel(); - node.state = AsyncDataTreeNodeState.Uninitialized; - this._onDidChangeNodeState.fire(node); - if (node !== this.root) { this.tree.collapse(node === this.root ? null : node); } throw err; + } finally { + if (node.slow) { + node.slow = false; + this._onDidChangeNodeSlowState.fire(node); + } } } @@ -646,7 +689,7 @@ export class AsyncDataTree implements IDisposable if (deep) { this.collapse(node.element.element as T); } else { - this.refreshNode(node.element, false, ChildrenResolutionReason.Expand); + this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand); } } } @@ -662,17 +705,19 @@ export class AsyncDataTree implements IDisposable } } - const children = childrenElements.map>>(element => { + const children = childrenElements.map>(element => { if (!this.identityProvider) { + const hasChildren = !!this.dataSource.hasChildren(element); + return { - element: { - element, - parent: node, - children: [], - state: AsyncDataTreeNodeState.Uninitialized - }, - collapsible: !!this.dataSource.hasChildren(element), - collapsed: true + element, + parent: node, + children: [], + state: AsyncDataTreeNodeState.Uninitialized, + hasChildren, + needsRefresh: false, + disposed: false, + slow: false }; } @@ -683,9 +728,13 @@ export class AsyncDataTree implements IDisposable const childAsyncDataTreeNode: IAsyncDataTreeNode = { element, parent: node, - id, children: [], - state: AsyncDataTreeNodeState.Uninitialized + id, + state: AsyncDataTreeNodeState.Uninitialized, + hasChildren: !!this.dataSource.hasChildren(element), + needsRefresh: false, + disposed: false, + slow: false }; if (viewStateContext) { @@ -698,69 +747,49 @@ export class AsyncDataTree implements IDisposable } } - return { - element: childAsyncDataTreeNode, - collapsible: !!this.dataSource.hasChildren(element), - collapsed: typeof viewStateContext === 'undefined' ? true : viewStateContext.viewState.expanded.indexOf(id) === -1 - }; + return childAsyncDataTreeNode; } asyncDataTreeNode.element = element; - const collapsible = !!this.dataSource.hasChildren(element); - const collapsed = !collapsible || this.tree.isCollapsed(asyncDataTreeNode === this.root ? null : asyncDataTreeNode); - - if (recursive) { - asyncDataTreeNode.state = AsyncDataTreeNodeState.Uninitialized; - - if (this.tree.isCollapsed(asyncDataTreeNode === this.root ? null : asyncDataTreeNode)) { - asyncDataTreeNode.children.length = 0; - - return { - element: asyncDataTreeNode, - collapsible, - collapsed - }; - } + if (asyncDataTreeNode.state === AsyncDataTreeNodeState.Loaded || asyncDataTreeNode.hasChildren !== !!this.dataSource.hasChildren(asyncDataTreeNode.element)) { + asyncDataTreeNode.needsRefresh = true; } - let children: Iterator>> | undefined = undefined; - - if (collapsible) { - children = Iterator.map(Iterator.fromArray(asyncDataTreeNode.children), asTreeElement); - } - - return { - element: asyncDataTreeNode, - children, - collapsible, - collapsed - }; + return asyncDataTreeNode; }); + // perf: if the node was and still is a leaf, avoid all these expensive no-ops + if (node.children.length === 0 && childrenElements.length === 0) { + return; + } + + node.children.splice(0, node.children.length, ...children); + } + + private render(node: IAsyncDataTreeNode, viewStateContext?: IAsyncDataTreeViewStateContext): void { const insertedElements = new Set(); const onDidCreateNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { insertedElements.add(treeNode.element.element as T); - this.nodes.set(treeNode.element.element as T, treeNode.element); + this.renderedNodes.set(treeNode.element.element as T, treeNode.element); } }; const onDidDeleteNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { if (!insertedElements.has(treeNode.element.element as T)) { - treeNode.element.state = AsyncDataTreeNodeState.Disposed; - this.nodes.delete(treeNode.element.element as T); + treeNode.element.disposed = true; + this.renderedNodes.delete(treeNode.element.element as T); } } }; - // 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)); - } + const children = node.children.map(c => asTreeElement(c, viewStateContext)); + this.tree.setChildren(node === this.root ? null : node, children, onDidCreateNode, onDidDeleteNode); + + this._onDidRender.fire(); } // view state @@ -781,7 +810,7 @@ export class AsyncDataTree implements IDisposable while (queue.length > 0) { const node = queue.shift()!; - if (node !== root && !node.collapsed) { + if (node !== root && node.collapsible && !node.collapsed) { expanded.push(getId(node.element!.element as T)); } diff --git a/src/vs/base/browser/ui/tree/media/tree.css b/src/vs/base/browser/ui/tree/media/tree.css index aa46437ecd7..4f86f971f97 100644 --- a/src/vs/base/browser/ui/tree/media/tree.css +++ b/src/vs/base/browser/ui/tree/media/tree.css @@ -57,6 +57,7 @@ .monaco-tl-twistie.loading { background-image: url("loading.svg"); + background-position: 0 center; } .vs-dark .monaco-tl-twistie.loading { From b10316e62f48da1c3689418ae24355a527829f03 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 15:22:45 +0100 Subject: [PATCH 0816/1066] slice the right array --- src/vs/platform/actions/common/actions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 6c8df2fabcd..c0b5b2f8879 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -182,7 +182,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry { } getMenuItems(id: MenuId): Array { - const result = this._menuItems[id].slice(0) || []; + const result = (this._menuItems[id] || []).slice(0); if (id === MenuId.CommandPalette) { // CommandPalette is special because it shows From cce8a050d2aeab05f38d19224147f4fd37e00e99 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 15:43:49 +0100 Subject: [PATCH 0817/1066] async data tree should prevent updateChildren, expand, before input is resolved --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index bfcb32022e6..2485cf1bd9e 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -401,6 +401,11 @@ export class AsyncDataTree implements IDisposable throw new Error('Tree input not set'); } + if (this.root.state === AsyncDataTreeNodeState.Loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } @@ -430,6 +435,15 @@ export class AsyncDataTree implements IDisposable } async expand(element: T, recursive: boolean = false): Promise { + if (typeof this.root.element === 'undefined') { + throw new Error('Tree input not set'); + } + + if (this.root.state === AsyncDataTreeNodeState.Loading) { + await this.subTreeRefreshPromises.get(this.root)!; + await Event.toPromise(this._onDidRender.event); + } + const node = this.getDataNode(element); if (node !== this.root && node.state !== AsyncDataTreeNodeState.Loading && !this.tree.isCollapsed(node)) { From 6b5bed6a851386d53e5cca6b736088d22c6db7fb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 15:51:07 +0100 Subject: [PATCH 0818/1066] fix #67495 --- .../smartSelect/test/smartSelect.test.ts | 33 +++++++++++++++++-- .../contrib/smartSelect/wordSelections.ts | 6 ++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 164a2314fed..99212b3b201 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -18,6 +18,7 @@ import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { BracketSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/bracketSelections'; import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { WordSelectionRangeProvider } from 'vs/editor/contrib/smartSelect/wordSelections'; class TestTextResourcePropertiesService implements ITextResourcePropertiesService { @@ -53,7 +54,8 @@ class MockJSMode extends MockMode { ['[', ']'] ], - onEnterRules: javascriptOnEnterRules + onEnterRules: javascriptOnEnterRules, + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g })); } } @@ -204,7 +206,7 @@ suite('SmartSelect', () => { assert.equal(expected.length, ranges!.length); for (const range of ranges!) { let exp = expected.shift() || null; - assert.ok(Range.equalsRange(range.range, exp), `A=${range} <> E=${exp}`); + assert.ok(Range.equalsRange(range.range, exp), `A=${range.range} <> E=${exp}`); } } @@ -253,4 +255,31 @@ suite('SmartSelect', () => { new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), ); }); + + test('in-word ranges', async () => { + + await assertRanges(new WordSelectionRangeProvider(), 'fIooBar', + new Range(1, 1, 1, 5), // foo + new Range(1, 1, 1, 8), // fooBar + new Range(1, 1, 1, 8), // line (triva) + new Range(1, 1, 1, 8), // line + new Range(1, 1, 1, 8), // doc + ); + + await assertRanges(new WordSelectionRangeProvider(), 'fIoo_Ba', + new Range(1, 1, 1, 5), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'fIoo-Ba', + new Range(1, 1, 1, 5), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + new Range(1, 1, 1, 8), + ); + }); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index efb30104bbc..512e0352654 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -42,6 +42,12 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { // ^^^^^^^ bucket.push({ range: new Range(pos.lineNumber, startColumn, pos.lineNumber, startColumn + offset), kind: 'statement.word.part' }); offset += 1; + } else if (ch === CharCode.Dash && lastCh !== CharCode.Dash) { + // foo-bar + // ^^^ + // ^^^^^^^ + bucket.push({ range: new Range(pos.lineNumber, startColumn, pos.lineNumber, startColumn + offset), kind: 'statement.word.part' }); + offset += 1; } lastCh = ch; } From 9606490e26f8cbe4d615305389d45e9b144ded7c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Jan 2019 15:55:26 +0100 Subject: [PATCH 0819/1066] fix #67245 (again) --- src/vs/platform/menubar/electron-main/menubar.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 7bda195c819..7070576c1c1 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -708,16 +708,18 @@ export class Menubar { } if (activeWindow) { - if (invocation.type === 'commandId') { - 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) { + if (isMacintosh && !this.environmentService.isBuilt && !activeWindow.isReady) { + if ((invocation.type === 'commandId' && invocation.commandId === 'workbench.action.toggleDevTools') || (invocation.type !== 'commandId' && invocation.userSettingsLabel === 'alt+cmd+i')) { // 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; } + } + if (invocation.type === 'commandId') { + this.windowsMainService.sendToFocused('vscode:runAction', { id: invocation.commandId, from: 'menu' } as IRunActionInWindowRequest); + } else { this.windowsMainService.sendToFocused('vscode:runKeybinding', { userSettingsLabel: invocation.userSettingsLabel } as IRunKeybindingInWindowRequest); } } From ab99f2ce88fa9388782413e34afc5dcc9dae3317 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 16:06:58 +0100 Subject: [PATCH 0820/1066] better fix for revealing active file after refresh --- .../parts/files/electron-browser/explorerService.ts | 9 ++++++++- .../parts/files/electron-browser/views/explorerView.ts | 7 +------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 8ac994aede6..0942fe7ad4b 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -17,6 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IExpression } from 'vs/base/common/glob'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; function getFileEventsExcludes(configurationService: IConfigurationService, root?: URI): IExpression { const scope = root ? { resource: root } : undefined; @@ -45,7 +46,8 @@ export class ExplorerService implements IExplorerService { @IInstantiationService private instantiationService: IInstantiationService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private contextService: IWorkspaceContextService, - @IClipboardService private clipboardService: IClipboardService + @IClipboardService private clipboardService: IClipboardService, + @IEditorService private editorService: IEditorService ) { } get roots(): ExplorerItem[] { @@ -158,6 +160,11 @@ export class ExplorerService implements IExplorerService { refresh(): void { this.model.roots.forEach(r => r.forgetChildren()); this._onDidChangeItem.fire(undefined); + const active = this.editorService.activeEditor; + if (active && active.getResource()) { + // We did a top level refresh, reveal the active file #67118 + this.select(active.getResource(), true); + } } // File events 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 ac741dd597f..9f4fcb57c13 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -407,12 +407,7 @@ export class ExplorerView extends ViewletPanel { const recursive = !item; const toRefresh = item || this.tree.getInput(); - return this.tree.updateChildren(toRefresh, recursive).then(() => { - if (!item) { - // We did a top level refresh, reveal the active file #67118 - this.selectActiveFile(true); - } - }); + return this.tree.updateChildren(toRefresh, recursive); } getOptimalWidth(): number { From f3c7479d6c881f3b2e1fe9d4e0d0b3d7f3b26c69 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 16:06:59 +0100 Subject: [PATCH 0821/1066] make word and bracket range provider default when no other providers exist, otherwise have word range provider only, #67490 --- src/vs/editor/contrib/smartSelect/smartSelect.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index cc62d7b0016..e483cbbb30c 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -204,13 +204,18 @@ registerEditorContribution(SmartSelectController); registerEditorAction(GrowSelectionAction); registerEditorAction(ShrinkSelectionAction); -modes.SelectionRangeRegistry.register('*', new WordSelectionRangeProvider()); -modes.SelectionRangeRegistry.register('*', new BracketSelectionRangeProvider()); - export function provideSelectionRanges(model: ITextModel, position: Position, token: CancellationToken): Promise { const provider = modes.SelectionRangeRegistry.orderedGroups(model); + if (provider.length === 0) { + // add word selection and bracket selection when no provider exists + provider.unshift([new WordSelectionRangeProvider(), new BracketSelectionRangeProvider()]); + } else { + // only word selections when other providers exist + provider.unshift([new WordSelectionRangeProvider()]); + } + interface RankedRange { rank: number; range: Range; From 00d1ec8778bd7fa6c5b4f49e4337516547deae78 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 16:13:23 +0100 Subject: [PATCH 0822/1066] fixes #65252 --- src/vs/platform/list/browser/listService.ts | 43 ++++++++++++++++--- .../parts/debug/browser/loadedScriptsView.ts | 2 +- .../debug/electron-browser/callStackView.ts | 2 +- .../electron-browser/views/explorerView.ts | 7 +++ .../markers/electron-browser/markersPanel.ts | 2 +- .../parts/search/browser/searchView.ts | 2 +- 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 515329e5de1..776395ccbcf 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -601,15 +601,14 @@ export interface IResourceResultsNavigationOptions { export class TreeResourceNavigator2 extends Disposable { - private readonly _openResource = new Emitter>(); - readonly openResource: Event> = this._openResource.event; + private readonly _onDidOpenResource = new Emitter>(); + readonly onDidOpenResource: Event> = this._onDidOpenResource.event; constructor( - private tree: WorkbenchObjectTree | WorkbenchAsyncDataTree, + private tree: WorkbenchObjectTree | WorkbenchDataTree | WorkbenchAsyncDataTree, private options?: IResourceResultsNavigationOptions ) { super(); - this.registerListeners(); } @@ -642,12 +641,15 @@ export class TreeResourceNavigator2 extends Disposable { } const isDoubleClick = e.browserEvent.detail === 2; - const sideBySide = e.browserEvent instanceof MouseEvent && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey); - this.open(!isDoubleClick, isDoubleClick, sideBySide); + + if (this.tree.openOnSingleClick || isDoubleClick) { + const sideBySide = e.browserEvent instanceof MouseEvent && (e.browserEvent.ctrlKey || e.browserEvent.metaKey || e.browserEvent.altKey); + this.open(!isDoubleClick, isDoubleClick, sideBySide); + } } private open(preserveFocus: boolean, pinned: boolean, sideBySide: boolean): void { - this._openResource.fire({ + this._onDidOpenResource.fire({ editorOptions: { preserveFocus, pinned, @@ -904,6 +906,7 @@ export class WorkbenchObjectTree, TFilterData = void> private hasDoubleSelection: IContextKey; private hasMultiSelection: IContextKey; + private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -940,6 +943,7 @@ export class WorkbenchObjectTree, TFilterData = void> this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); + this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); this.disposables.push( @@ -961,6 +965,9 @@ export class WorkbenchObjectTree, TFilterData = void> this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(openModeSettingKey)) { + this._openOnSingleClick = useSingleClickToOpen(configurationService); + } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } @@ -979,6 +986,10 @@ export class WorkbenchObjectTree, TFilterData = void> ); } + get openOnSingleClick(): boolean { + return this._openOnSingleClick; + } + get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } @@ -997,6 +1008,7 @@ export class WorkbenchDataTree extends DataTree; private hasMultiSelection: IContextKey; + private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -1034,6 +1046,7 @@ export class WorkbenchDataTree extends DataTree extends DataTree 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(openModeSettingKey)) { + this._openOnSingleClick = useSingleClickToOpen(configurationService); + } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } @@ -1073,6 +1089,10 @@ export class WorkbenchDataTree extends DataTree extends Async private hasDoubleSelection: IContextKey; private hasMultiSelection: IContextKey; + private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -1123,6 +1144,7 @@ export class WorkbenchAsyncDataTree extends Async this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); + this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); this.disposables.push( @@ -1144,6 +1166,9 @@ export class WorkbenchAsyncDataTree extends Async this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0); }), configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(openModeSettingKey)) { + this._openOnSingleClick = useSingleClickToOpen(configurationService); + } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); } @@ -1162,6 +1187,10 @@ export class WorkbenchAsyncDataTree extends Async ); } + get openOnSingleClick(): boolean { + return this._openOnSingleClick; + } + get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index fabd77cd8c0..c59a90734ca 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -429,7 +429,7 @@ export class LoadedScriptsView extends ViewletPanel { const loadedScriptsNavigator = new TreeResourceNavigator2(this.tree); this.disposables.push(loadedScriptsNavigator); - this.disposables.push(loadedScriptsNavigator.openResource(e => { + this.disposables.push(loadedScriptsNavigator.onDidOpenResource(e => { if (e.element instanceof BaseTreeItem) { const source = e.element.getSource(); if (source && source.available) { diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index aef0de79a27..a4dfb9bea3c 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -149,7 +149,7 @@ export class CallStackView extends ViewletPanel { const callstackNavigator = new TreeResourceNavigator2(this.tree); this.disposables.push(callstackNavigator); - this.disposables.push(callstackNavigator.openResource(e => { + this.disposables.push(callstackNavigator.onDidOpenResource(e => { if (this.ignoreSelectionChangedEvent) { return; } 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 9f4fcb57c13..4f3b10f4513 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -289,6 +289,8 @@ export class ExplorerView extends ViewletPanel { this.rootContext.set(!stat || (stat && stat.isRoot)); })); + // TODO@Isidor: use TreeResourceNavigator2 just like search and listen to the `onDidOpenResource` instead + // Open when selecting via keyboard this.disposables.push(this.tree.onDidChangeSelection(e => { if (!e.browserEvent) { @@ -318,6 +320,11 @@ export class ExplorerView extends ViewletPanel { if (e.browserEvent instanceof MouseEvent) { isDoubleClick = e.browserEvent.detail === 2; + + if (!this.tree.openOnSingleClick && !isDoubleClick) { + return; + } + isMiddleClick = e.browserEvent.button === 1; sideBySide = this.tree.useAltAsMultipleSelectionModifier ? (e.browserEvent.ctrlKey || e.browserEvent.metaKey) : e.browserEvent.altKey; } diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index 128c751cd08..c86ac4dca7c 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -354,7 +354,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { })); const markersNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true })); - this._register(Event.debounce(markersNavigator.openResource, (last, event) => event, 75, true)(options => { + this._register(Event.debounce(markersNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => { this.openFileAtElement(options.element, options.editorOptions.preserveFocus, options.sideBySide, options.editorOptions.pinned); })); this._register(this.tree.onDidChangeCollapseState(({ node }) => { diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index a6ef7c274c9..25a2ec1b093 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -638,7 +638,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const resourceNavigator = this._register(new TreeResourceNavigator2(this.tree, { openOnFocus: true })); - this._register(Event.debounce(resourceNavigator.openResource, (last, event) => event, 75, true)(options => { + this._register(Event.debounce(resourceNavigator.onDidOpenResource, (last, event) => event, 75, true)(options => { if (options.element instanceof Match) { const selectedMatch: Match = options.element; if (this.currentSelectedFileMatch) { From 47bf2388965ae797a0b9556ffa90132dfdede1bc Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 16:19:10 +0100 Subject: [PATCH 0823/1066] better fix for #67495 --- src/vs/editor/contrib/smartSelect/smartSelect.ts | 10 +++++----- .../contrib/smartSelect/test/smartSelect.test.ts | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index e483cbbb30c..19ed2246954 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -204,16 +204,16 @@ registerEditorContribution(SmartSelectController); registerEditorAction(GrowSelectionAction); registerEditorAction(ShrinkSelectionAction); +// word selection +modes.SelectionRangeRegistry.register('*', new WordSelectionRangeProvider()); + export function provideSelectionRanges(model: ITextModel, position: Position, token: CancellationToken): Promise { const provider = modes.SelectionRangeRegistry.orderedGroups(model); - if (provider.length === 0) { + if (provider.length === 1) { // add word selection and bracket selection when no provider exists - provider.unshift([new WordSelectionRangeProvider(), new BracketSelectionRangeProvider()]); - } else { - // only word selections when other providers exist - provider.unshift([new WordSelectionRangeProvider()]); + provider.unshift([new BracketSelectionRangeProvider()]); } interface RankedRange { diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index 99212b3b201..efe1e8d2056 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -282,4 +282,8 @@ suite('SmartSelect', () => { new Range(1, 1, 1, 8), ); }); + + test('Default selection should select current word/hump first in camelCase #67493', function () { + + }); }); From c47ea5b61f70fd08f60e7a5b0d4229707aed00e3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 16:36:53 +0100 Subject: [PATCH 0824/1066] fixes #67233 --- src/vs/base/browser/ui/tree/indexTreeModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 3df971f5b55..bea50064994 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -95,7 +95,7 @@ export class IndexTreeModel, TFilterData = voi throw new Error('Invalid tree location'); } - const { parentNode, listIndex, revealed } = this.getParentNodeWithListIndex(location); + const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const treeListElementsToInsert: ITreeNode[] = []; const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); @@ -110,7 +110,7 @@ export class IndexTreeModel, TFilterData = voi const lastIndex = location[location.length - 1]; const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); - if (revealed) { + if (revealed && visible) { const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0); this._updateAncestorsRenderNodeCount(parentNode, renderNodeCount - visibleDeleteCount); From 245dce024977145ea5f366dd5639ad11d0fc5572 Mon Sep 17 00:00:00 2001 From: isidor Date: Wed, 30 Jan 2019 16:32:01 +0100 Subject: [PATCH 0825/1066] strickt null --- .../parts/files/electron-browser/explorerService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 0942fe7ad4b..234117fd39c 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -160,10 +160,10 @@ export class ExplorerService implements IExplorerService { refresh(): void { this.model.roots.forEach(r => r.forgetChildren()); this._onDidChangeItem.fire(undefined); - const active = this.editorService.activeEditor; - if (active && active.getResource()) { + const resource = this.editorService.activeEditor ? this.editorService.activeEditor.getResource() : undefined; + if (resource) { // We did a top level refresh, reveal the active file #67118 - this.select(active.getResource(), true); + this.select(resource, true); } } From 4d9677a76131d54dde29c693be6207f00a2d443e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 16:42:29 +0100 Subject: [PATCH 0826/1066] fixes #67279 --- src/vs/base/browser/ui/list/listWidget.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 87da7ffcb11..2cc18d00b3a 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -351,7 +351,9 @@ class TypeLabelController implements IDisposable { } private onDidUpdateListOptions(options: IListOptions): void { - if (options.enableKeyboardNavigation) { + const enableKeyboardNavigation = typeof options.enableKeyboardNavigation === 'undefined' ? true : !!options.enableKeyboardNavigation; + + if (enableKeyboardNavigation) { this.enable(); } else { this.disable(); From 1079ea5a08df478e5f960124304d4d32ffc9ea7d Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 16:48:26 +0100 Subject: [PATCH 0827/1066] fixes #67083 --- src/vs/base/browser/ui/tree/abstractTree.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index baf6d4ba919..e71bf94434c 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -485,6 +485,17 @@ class TypeFilterController implements IDisposable { this.filter.pattern = pattern; this.tree.refilter(); this.tree.focusNext(0, true); + + const focus = this.tree.getFocus(); + + if (focus.length > 0) { + const element = focus[0]; + + if (this.tree.getRelativeTop(element) === null) { + this.tree.reveal(focus[0], 0.5); + } + } + this.render(); } From 7e1724d8a58524b2e2508b2d53589c4ccb9c50e6 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 16:54:11 +0100 Subject: [PATCH 0828/1066] disable horizontal scrolling for scm repositories related to #67378 --- src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 2f7895892bb..1d0d52e3c95 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -260,7 +260,7 @@ class MainPanel extends ViewletPanel { const renderer = this.instantiationService.createInstance(ProviderRenderer); const identityProvider = { getId: r => r.provider.id }; - this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { identityProvider }) as WorkbenchList; + this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { identityProvider, horizontalScrolling: false }) as WorkbenchList; this.list.onSelectionChange(this.onListSelectionChange, this, this.disposables); this.list.onContextMenu(this.onListContextMenu, this, this.disposables); From 07055a5f2112e26e000ccd7703a0be223ad0db4b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 17:02:40 +0100 Subject: [PATCH 0829/1066] fixes #67395 --- src/vs/base/browser/ui/tree/abstractTree.ts | 22 ++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index e71bf94434c..1ff4a7f6dc0 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -363,11 +363,12 @@ class TypeFilter implements ITreeFilter, IDisposable { class TypeFilterController implements IDisposable { - get pattern(): string { - return this._pattern; - } + private _enabled = false; + get enabled(): boolean { return this._enabled; } + + private _pattern = ''; + get pattern(): string { return this._pattern; } - private enabled = false; private positionClassName = 'ne'; private domNode: HTMLElement; private messageDomNode: HTMLElement; @@ -375,7 +376,6 @@ class TypeFilterController implements IDisposable { private filterOnTypeDomNode: HTMLInputElement; private clearDomNode: HTMLElement; - private _pattern = ''; private enabledDisposables: IDisposable[] = []; private disposables: IDisposable[] = []; @@ -425,7 +425,7 @@ class TypeFilterController implements IDisposable { } private enable(): void { - if (this.enabled) { + if (this._enabled) { return; } @@ -456,11 +456,11 @@ class TypeFilterController implements IDisposable { this.filter.pattern = ''; this.tree.refilter(); this.render(); - this.enabled = true; + this._enabled = true; } private disable(): void { - if (!this.enabled) { + if (!this._enabled) { return; } @@ -468,7 +468,7 @@ class TypeFilterController implements IDisposable { this.enabledDisposables = dispose(this.enabledDisposables); this.tree.refilter(); this.render(); - this.enabled = false; + this._enabled = false; } private onInput(pattern: string): void { @@ -559,7 +559,7 @@ class TypeFilterController implements IDisposable { } private onDidSpliceModel(): void { - if (!this.enabled || this.pattern.length === 0) { + if (!this._enabled || this.pattern.length === 0) { return; } @@ -718,7 +718,7 @@ export abstract class AbstractTree implements IDisposable if (_options.keyboardNavigationLabelProvider) { const typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); this.focusNavigationFilter = node => { - if (!typeFilterController.pattern) { + if (!typeFilterController.enabled || !typeFilterController.pattern) { return true; } From 8e3595ae0f492629aad883df59bcd9eed86df808 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 17:10:22 +0100 Subject: [PATCH 0830/1066] fixes #67376 --- .../editor/contrib/referenceSearch/referencesWidget.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index 1978ad1e501..d238db4cd88 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -253,6 +253,8 @@ export class ReferenceWidget extends PeekViewWidget { private _previewNotAvailableMessage: TextModel; private _previewContainer: HTMLElement; private _messageContainer: HTMLElement; + private height: number | undefined; + private width: number | undefined; constructor( editor: ICodeEditor, @@ -346,7 +348,7 @@ export class ReferenceWidget extends PeekViewWidget { this._previewContainer.style.width = left; this._treeContainer.style.width = right; this._preview.layout(); - this._tree.layout(); + this._tree.layout(this.height, this.width && this.width * (1 - this._sash.ratio)); this.layoutData.ratio = this._sash.ratio; }); @@ -417,6 +419,9 @@ export class ReferenceWidget extends PeekViewWidget { protected _doLayoutBody(heightInPixel: number, widthInPixel: number): void { super._doLayoutBody(heightInPixel, widthInPixel); + this.height = heightInPixel; + this.width = widthInPixel; + const height = heightInPixel + 'px'; this._sash.height = heightInPixel; this._sash.width = widthInPixel; @@ -428,7 +433,8 @@ export class ReferenceWidget extends PeekViewWidget { this._treeContainer.style.height = height; this._treeContainer.style.width = right; // forward - this._tree.layout(heightInPixel, widthInPixel); + console.log(widthInPixel, this._sash.ratio, widthInPixel * (1 - this._sash.ratio)); + this._tree.layout(heightInPixel, widthInPixel * (1 - this._sash.ratio)); this._preview.layout(); // store layout data From 07ab562666e3044feaab5a072948009257e5874b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 17:17:21 +0100 Subject: [PATCH 0831/1066] fixes #67378 --- .../parts/scm/electron-browser/scmViewlet.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts index 1d0d52e3c95..920cc42b0a6 100644 --- a/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts +++ b/src/vs/workbench/parts/scm/electron-browser/scmViewlet.ts @@ -143,6 +143,9 @@ class ProviderRenderer implements IListRenderer(); + readonly onDidRenderElement = this._onDidRenderElement.event; + constructor( @ICommandService protected commandService: ICommandService, @IThemeService protected themeService: IThemeService @@ -197,6 +200,8 @@ class ProviderRenderer implements IListRenderer r.provider.id }; - this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { identityProvider, horizontalScrolling: false }) as WorkbenchList; + this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [renderer], { identityProvider }) as WorkbenchList; + renderer.onDidRenderElement(e => this.list.updateWidth(this.viewModel.repositories.indexOf(e)), null, this.disposables); this.list.onSelectionChange(this.onListSelectionChange, this, this.disposables); this.list.onContextMenu(this.onListContextMenu, this, this.disposables); @@ -293,8 +299,8 @@ class MainPanel extends ViewletPanel { } } - protected layoutBody(size: number): void { - this.list.layout(size); + protected layoutBody(height: number, width: number): void { + this.list.layout(height, width); } private updateBodySize(): void { From 8059a5895f6593389740d956ee56de0d318e4136 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Jan 2019 16:28:45 +0100 Subject: [PATCH 0832/1066] Improve doc comment --- 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 62aabce3dba..f9d86d51263 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1058,7 +1058,8 @@ declare module 'vscode' { //#region Alex - extensions.all change event export namespace extensions { /** - * An event which fires when `extensions.all` changes. + * An event which fires when `extensions.all` changes. This can happen when extensions are + * installed, uninstalled, enabled or disabled. */ export const onDidChange: Event; } From 26ed3e96dc08260fb4b250001f908dc234e9518a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Jan 2019 17:09:01 +0100 Subject: [PATCH 0833/1066] Fixes #67326: Update trie when adding / removing extensions --- .../api/node/extHostExtensionService.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 8edbff284ea..76a04e06978 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -669,8 +669,25 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { ); } - public $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { + public async $deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise { toAdd.forEach((extension) => (extension).extensionLocation = URI.revive(extension.extensionLocation)); + + const trie = await this.getExtensionPathIndex(); + + await Promise.all(toRemove.map(async (extensionId) => { + const extensionDescription = this._registry.getExtensionDescription(extensionId); + if (!extensionDescription) { + return; + } + const realpath = await pfs.realpath(extensionDescription.extensionLocation.fsPath); + trie.delete(URI.file(realpath).fsPath); + })); + + await Promise.all(toAdd.map(async (extensionDescription) => { + const realpath = await pfs.realpath(extensionDescription.extensionLocation.fsPath); + trie.set(URI.file(realpath).fsPath, extensionDescription); + })); + this._registry.deltaExtensions(toAdd, toRemove); return Promise.resolve(undefined); } From 520ae76bc5c870a43e239cc814a9cf940809f3c8 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Jan 2019 17:10:10 +0100 Subject: [PATCH 0834/1066] Move extensions.onDidChange to finalized API --- src/vs/vscode.d.ts | 6 ++++++ src/vs/vscode.proposed.d.ts | 10 ---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index b2d1771d638..42de798fe0e 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -8813,6 +8813,12 @@ declare module 'vscode' { * All extensions currently known to the system. */ export let all: Extension[]; + + /** + * An event which fires when `extensions.all` changes. This can happen when extensions are + * installed, uninstalled, enabled or disabled. + */ + export const onDidChange: Event; } } diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f9d86d51263..99761bd3fb4 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1055,16 +1055,6 @@ declare module 'vscode' { } //#endregion - //#region Alex - extensions.all change event - export namespace extensions { - /** - * An event which fires when `extensions.all` changes. This can happen when extensions are - * installed, uninstalled, enabled or disabled. - */ - export const onDidChange: Event; - } - //#endregion - //#region Tree View export interface TreeView { From 15e77e8cf8dd8d2c67f679909e2d572f34e2925c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Jan 2019 17:33:39 +0100 Subject: [PATCH 0835/1066] fix #66936 --- src/vs/workbench/api/node/extHostExtensionService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/api/node/extHostExtensionService.ts b/src/vs/workbench/api/node/extHostExtensionService.ts index 76a04e06978..1f4a1e100b0 100644 --- a/src/vs/workbench/api/node/extHostExtensionService.ts +++ b/src/vs/workbench/api/node/extHostExtensionService.ts @@ -591,6 +591,10 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape { return Promise.resolve(undefined); } + if (this._initData.autoStart) { + return Promise.resolve(undefined); // https://github.com/Microsoft/vscode/issues/66936 + } + // Require the test runner via node require from the provided path let testRunner: ITestRunner; let requireError: Error; From f62abe488876e32643270c0be55b0800da0d84ff Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Jan 2019 17:57:34 +0100 Subject: [PATCH 0836/1066] fix #67246 --- .../files/browser/editors/textFileEditor.ts | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index 09aaa5edc68..dceca11ee4d 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -11,7 +11,7 @@ import { Action } from 'vs/base/common/actions'; import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/parts/files/common/files'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; -import { EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -99,11 +99,22 @@ export class TextFileEditor extends BaseTextEditor { // 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.groupListener = dispose(this.groupListener); - this.groupListener = ((group as IEditorGroupView).onWillCloseEditor(e => { - if (e.editor === this.input) { - this.doSaveOrClearTextEditorViewState(this.input); - } - })); + this.groupListener = ((group as IEditorGroupView).onWillCloseEditor(e => this.onWillCloseEditorInGroup(e))); + } + + private onWillCloseEditorInGroup(e: IEditorCloseEvent): void { + const editor = e.editor; + if (!(editor instanceof FileEditorInput)) { + return; // only handle files + } + + // If the editor is currently active we can always save or clear the view state. + // If the editor is not active, we can only clear the view state because it needs + // an active editor with the file opened, so we check for the restoreViewState flag + // being set. + if (editor === this.input || !this.restoreViewState) { + this.doSaveOrClearTextEditorViewState(editor); + } } setOptions(options: EditorOptions): void { From 8eade79bcccabbe73232f20deead26ed26610ef3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Jan 2019 18:03:21 +0100 Subject: [PATCH 0837/1066] call it openExternal, #67344 --- src/vs/vscode.d.ts | 2 +- src/vs/workbench/api/node/extHost.api.impl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 42de798fe0e..e68bdf1b04f 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -5932,7 +5932,7 @@ declare module 'vscode' { * @param target The uri that should be opened. * @returns A promise indicating if open was successful. */ - export function open(target: Uri): Thenable; + export function openExternal(target: Uri): Thenable; } /** diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index ebdf93c02d4..02fd3f33913 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -256,7 +256,7 @@ export function createApiFactory( get clipboard(): vscode.Clipboard { return extHostClipboard; }, - open(uri: URI) { + openExternal(uri: URI) { return extHostWindow.openUri(uri); } }); From 6d02b80403cfe96a416e36a55b5905061597d2f2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 30 Jan 2019 18:14:04 +0100 Subject: [PATCH 0838/1066] Fixes #67391: fix delta computation --- .../workbench/services/extensions/common/extensionsRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 029a08153ba..41e381496d8 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -84,7 +84,7 @@ export class ExtensionPointUserDelta { return new ExtensionPointUserDelta(current, []); } if (!current || !current.length) { - return new ExtensionPointUserDelta([], current); + return new ExtensionPointUserDelta([], previous); } const previousSet = this._toSet(previous); From 699ee7e1cf8890b8c38fedc9f3d8046a714a327a Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 18:38:26 +0100 Subject: [PATCH 0839/1066] fixes #67314 --- src/vs/base/browser/ui/list/listView.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 078c3e50227..40c16806df2 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -959,12 +959,15 @@ export class ListView implements ISpliceable, IDisposable { // Let's remember the second element's position, this helps in scrolling up // and preserving a linear upwards scroll movement - let secondElementIndex: number | undefined; - let secondElementTopDelta: number | undefined; + let anchorElementIndex: number | undefined; + let anchorElementTopDelta: number | undefined; - if (previousRenderRange.end - previousRenderRange.start > 1) { - secondElementIndex = previousRenderRange.start + 1; - secondElementTopDelta = this.elementTop(secondElementIndex) - renderTop; + if (renderTop === this.elementTop(previousRenderRange.start)) { + anchorElementIndex = previousRenderRange.start; + anchorElementTopDelta = 0; + } else if (previousRenderRange.end - previousRenderRange.start > 1) { + anchorElementIndex = previousRenderRange.start + 1; + anchorElementTopDelta = this.elementTop(anchorElementIndex) - renderTop; } let heightDiff = 0; @@ -1017,8 +1020,8 @@ export class ListView implements ISpliceable, IDisposable { } } - if (typeof secondElementIndex === 'number') { - this.scrollTop = this.elementTop(secondElementIndex) - secondElementTopDelta!; + if (typeof anchorElementIndex === 'number') { + this.scrollTop = this.elementTop(anchorElementIndex) - anchorElementTopDelta!; } this._onDidChangeContentHeight.fire(this.contentHeight); From 5710af7264ae04f51f408fe91f7e61c249db9345 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Jan 2019 09:38:13 -0800 Subject: [PATCH 0840/1066] update platform css structure fixes #54049 --- src/vs/workbench/electron-browser/media/shell.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index 47e9490b7d2..e3e3172eafe 100644 --- a/src/vs/workbench/electron-browser/media/shell.css +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -122,7 +122,7 @@ margin-bottom: 0.2em; } -.monaco-shell .linux .monaco-menu .monaco-action-bar.vertical .action-label.separator { +.monaco-shell.linux .monaco-menu .monaco-action-bar.vertical .action-label.separator { margin-left: 0; margin-right: 0; } @@ -132,7 +132,7 @@ padding: 0 1.8em; } -.monaco-shell .linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { +.monaco-shell.linux .monaco-menu .monaco-action-bar.vertical .submenu-indicator { height: 100%; -webkit-mask-size: 10px 10px; mask-size: 10px 10px; From 1ef0aa12cbb0d2a8bd16fb83a054603edbd64b45 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 30 Jan 2019 10:00:24 -0800 Subject: [PATCH 0841/1066] Fix old code in proposed API --- 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 99761bd3fb4..e8d0f99c563 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -997,7 +997,7 @@ declare module 'vscode' { * terminalRenderer.onDidAcceptInput(data => { * console.log(data); // 'Hello world' * }); - * terminalRenderer.terminal.then(t => t.sendText('Hello world')); + * terminalRenderer.terminal.sendText('Hello world'); * ``` */ readonly onDidAcceptInput: Event; From 351dc7377fc55b3f4a1846aeb4bb9864a9138189 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Jan 2019 10:04:38 -0800 Subject: [PATCH 0842/1066] mitigation for #66796 --- .../electron-browser/settingsEditor2.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index b177f2ce4f0..30d99008bc7 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -549,22 +549,24 @@ export class SettingsEditor2 extends BaseEditor { this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { - 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; + setTimeout(() => { + const element: SettingsTreeGroupElement = e.elements[0]; + if (this.tocFocusedElement === e.elements[0]) { + return; } - } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { - this.settingsTree.reveal(element, 0); - } + + 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 && (!e.browserEvent || !(e.browserEvent).fromScroll)) { + this.settingsTree.reveal(element, 0); + } + }, 0); })); this._register(this.tocTree.onDidFocus(() => { From 7ca3f253db3371604e390ef1ceca88cc6dff95f5 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Jan 2019 11:07:20 -0800 Subject: [PATCH 0843/1066] fixes custom viewlet icons in activity bar with grid layout update monaco-workbench > CSS --- .../api/browser/viewsExtensionPoint.ts | 2 +- .../parts/activitybar/activitybarActions.ts | 34 +++++++++---------- .../parts/editor/media/notabstitlecontrol.css | 2 +- .../debug/browser/media/debugViewlet.css | 2 +- .../page/electron-browser/welcomePage.css | 12 +++---- .../electron-browser/walkThroughPart.css | 6 ++-- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 6ab333bf1e4..fbffb5d002f 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -326,7 +326,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { ); // Generate CSS to show the icon in the activity bar - const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${cssClass}`; + const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`; createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%`); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 2fe84cf2ba5..2144bb90f7e 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -175,7 +175,7 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction { ) { super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, partService, telemetryService); - const iconClass = `.monaco-workbench > .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar + const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl || ''}') no-repeat 50% 50%`); } @@ -271,9 +271,9 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const activeForegroundColor = theme.getColor(ACTIVITY_BAR_FOREGROUND); if (activeForegroundColor) { collector.addRule(` - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active .action-label, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus .action-label, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover .action-label { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.active .action-label, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:focus .action-label, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:hover .action-label { background-color: ${activeForegroundColor} !important; } `); @@ -283,7 +283,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const outline = theme.getColor(activeContrastBorder); if (outline) { collector.addRule(` - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:before { content: ""; position: absolute; top: 9px; @@ -292,26 +292,26 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { width: 32px; } - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.active:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.active:hover:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.checked:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.checked:hover:before { outline: 1px solid; } - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:hover:before { outline: 1px dashed; } - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:focus:before { border-left-color: ${outline}; } - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before, - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.active:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.active:hover:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.checked:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item.checked:hover:before, + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:hover:before { outline-color: ${outline}; } `); @@ -322,7 +322,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { collector.addRule(` - .monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before { + .monaco-workbench .activitybar > .content .monaco-action-bar .action-item:focus:before { border-left-color: ${focusBorderColor}; } `); diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 066987b790b..1190f855b69 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -54,7 +54,7 @@ background-image: none; } -.windows > .monaco-workbench > .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { content: '\\'; } diff --git a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css index 7cf772fb7b0..9d27545f44b 100644 --- a/src/vs/workbench/parts/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/parts/debug/browser/media/debugViewlet.css @@ -64,7 +64,7 @@ margin-top: 7px; } -.mac > .monaco-workbench > .part > .title > .title-actions .start-debug-action-item { +.mac > .monaco-workbench .part > .title > .title-actions .start-debug-action-item { border-radius: 4px; } 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 75a18c03da3..a24da657f52 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.css @@ -228,21 +228,21 @@ .monaco-workbench .part.editor > .content .welcomePage .linux-only { display: none; } -.mac > .monaco-workbench > .part.editor > .content .welcomePage .mac-only { +.mac > .monaco-workbench .part.editor > .content .welcomePage .mac-only { display: initial; } -.windows > .monaco-workbench > .part.editor > .content .welcomePage .windows-only { +.windows > .monaco-workbench .part.editor > .content .welcomePage .windows-only { display: initial; } -.linux > .monaco-workbench > .part.editor > .content .welcomePage .linux-only { +.linux > .monaco-workbench .part.editor > .content .welcomePage .linux-only { display: initial; } -.mac > .monaco-workbench > .part.editor > .content .welcomePage li.mac-only { +.mac > .monaco-workbench .part.editor > .content .welcomePage li.mac-only { display: list-item; } -.windows > .monaco-workbench > .part.editor > .content .welcomePage li.windows-only { +.windows > .monaco-workbench .part.editor > .content .welcomePage li.windows-only { display: list-item; } -.linux > .monaco-workbench > .part.editor > .content .welcomePage li.linux-only { +.linux > .monaco-workbench .part.editor > .content .welcomePage li.linux-only { display: list-item; } 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 c83ac377cae..2194a2a98c7 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css +++ b/src/vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart.css @@ -123,13 +123,13 @@ .monaco-workbench .part.editor > .content .walkThroughContent .linux-only { display: none; } -.mac > .monaco-workbench > .part.editor > .content .walkThroughContent .mac-only { +.mac > .monaco-workbench .part.editor > .content .walkThroughContent .mac-only { display: initial; } -.windows > .monaco-workbench > .part.editor > .content .walkThroughContent .windows-only { +.windows > .monaco-workbench .part.editor > .content .walkThroughContent .windows-only { display: initial; } -.linux > .monaco-workbench > .part.editor > .content .walkThroughContent .linux-only { +.linux > .monaco-workbench .part.editor > .content .walkThroughContent .linux-only { display: initial; } From f337fa838e23c77b0233cbbc28c40d4f2c2c6cf8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 11:23:20 -0800 Subject: [PATCH 0844/1066] Change windows keybinding of the auto fix command Fixes #67411 --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 81ffd354aab..02527482401 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -395,7 +395,10 @@ export class AutoFixAction extends EditorAction { contextKeyForSupportedActions(CodeActionKind.QuickFix)), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_DOT, + primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.US_DOT, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_DOT + }, weight: KeybindingWeight.EditorContrib } }); From 3b5538c31fe0ab13265f17a6dbbbd307af2d3323 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 11:23:46 -0800 Subject: [PATCH 0845/1066] Mark that auto fix can show a menu --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 02527482401..7eb09212d89 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -388,7 +388,7 @@ export class AutoFixAction extends EditorAction { constructor() { super({ id: AutoFixAction.Id, - label: nls.localize('autoFix.label', "Auto Fix"), + label: nls.localize('autoFix.label', "Auto Fix..."), alias: 'Auto Fix', precondition: ContextKeyExpr.and( EditorContextKeys.writable, From d17fc40536418cb15c75288bf3629a3b8e1d6531 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 11:24:43 -0800 Subject: [PATCH 0846/1066] Fix name --- .../api/electron-browser/mainThreadSaveParticipant.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 783a76ab020..fb211f7de79 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -290,7 +290,7 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { } } -class CodeActionOnParticipant implements ISaveParticipant { +class CodeActionOnSaveParticipant implements ISaveParticipant { constructor( @IBulkEditService private readonly _bulkEditService: IBulkEditService, @@ -404,7 +404,7 @@ export class SaveParticipant implements ISaveParticipant { ) { this._saveParticipants = new IdleValue(() => [ instantiationService.createInstance(TrimWhitespaceParticipant), - instantiationService.createInstance(CodeActionOnParticipant), + instantiationService.createInstance(CodeActionOnSaveParticipant), instantiationService.createInstance(FormatOnSaveParticipant), instantiationService.createInstance(FinalNewLineParticipant), instantiationService.createInstance(TrimFinalNewLinesParticipant), From f0d1dbcd2c976ce9e7d845d01a61992b6a5f2d5f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 11:32:44 -0800 Subject: [PATCH 0847/1066] On save, always run fixAll before other source actions Fixes #67482 --- .../editor/contrib/codeAction/codeActionTrigger.ts | 1 + .../electron-browser/mainThreadSaveParticipant.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts index d782e836367..15e90604ed6 100644 --- a/src/vs/editor/contrib/codeAction/codeActionTrigger.ts +++ b/src/vs/editor/contrib/codeAction/codeActionTrigger.ts @@ -14,6 +14,7 @@ export class CodeActionKind { public static readonly Refactor = new CodeActionKind('refactor'); public static readonly Source = new CodeActionKind('source'); public static readonly SourceOrganizeImports = new CodeActionKind('source.organizeImports'); + public static readonly SourceFixAll = new CodeActionKind('source.fixAll'); constructor( public readonly value: string diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index fb211f7de79..8682ee6a2ed 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -311,7 +311,17 @@ class CodeActionOnSaveParticipant implements ISaveParticipant { return undefined; } - const codeActionsOnSave = Object.keys(setting).filter(x => setting[x]).map(x => new CodeActionKind(x)); + const codeActionsOnSave = Object.keys(setting) + .filter(x => setting[x]).map(x => new CodeActionKind(x)) + .sort((a, b) => { + if (a.value === CodeActionKind.SourceFixAll.value) { + return -1; + } + if (b.value === CodeActionKind.SourceFixAll.value) { + return 1; + } + return 0; + }); if (!codeActionsOnSave.length) { return undefined; } From 6787bd1f2593434da7961132bea56f9954bea266 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 30 Jan 2019 11:46:57 -0800 Subject: [PATCH 0848/1066] Update html/css custom data config schema for #67544 --- extensions/css-language-features/package.json | 3 +++ extensions/html-language-features/package.json | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 514196d3608..79b32dd352b 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -37,6 +37,9 @@ "type": "array", "description": "A list of JSON file paths that define custom CSS data that loads custom properties, at directives, pseudo classes / elements.", "default": [], + "items": { + "type": "string" + }, "scope": "resource" }, "css.validate": { diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 0fc7ed3ce11..9449c662605 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -34,7 +34,12 @@ "properties": { "html.experimental.customData": { "type": "array", - "description": "A list of JSON file paths that define custom tags, properties and other HTML syntax constructs. Only workspace folder setting will be read." + "description": "A list of JSON file paths that define custom tags, properties and other HTML syntax constructs. Only workspace folder setting will be read.", + "default": [], + "items": { + "type": "string" + }, + "scope": "resource" }, "html.format.enable": { "type": "boolean", From af57ecdf9d303b2b1a3e93caf285795ba0550cab Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 30 Jan 2019 11:58:42 -0800 Subject: [PATCH 0849/1066] Fix #67544 --- .../client/src/customData.ts | 11 ++++++++--- .../client/src/customData.ts | 19 ++++++++++++++----- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/extensions/css-language-features/client/src/customData.ts b/extensions/css-language-features/client/src/customData.ts index 8ba0770235a..b6db79db75d 100644 --- a/extensions/css-language-features/client/src/customData.ts +++ b/extensions/css-language-features/client/src/customData.ts @@ -28,9 +28,14 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol wfCSSConfig.workspaceFolderValue.experimental && wfCSSConfig.workspaceFolderValue.experimental.customData ) { - wfCSSConfig.workspaceFolderValue.experimental.customData.forEach(p => [ - dataPaths.push(path.resolve(wf.uri.fsPath, p)) - ]); + const customData = wfCSSConfig.workspaceFolderValue.experimental.customData; + if (Array.isArray(customData)) { + customData.forEach(t => { + if (typeof t === 'string') { + dataPaths.push(path.resolve(wf.uri.fsPath, t)); + } + }); + } } }); diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index 8e9bf2863b6..2c8c7615a35 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -26,11 +26,15 @@ export function getCustomDataPathsInAllWorkspaces(workspaceFolders: WorkspaceFol if ( wfHtmlConfig && wfHtmlConfig.workspaceFolderValue && - wfHtmlConfig.workspaceFolderValue.experimental + wfHtmlConfig.workspaceFolderValue.experimental && + wfHtmlConfig.workspaceFolderValue.experimental.customData ) { - if (wfHtmlConfig.workspaceFolderValue.experimental.customData) { - wfHtmlConfig.workspaceFolderValue.experimental.customData.forEach(t => { - dataPaths.push(path.resolve(wf.uri.fsPath, t)); + const customData = wfHtmlConfig.workspaceFolderValue.experimental.customData; + if (Array.isArray(customData)) { + customData.forEach(t => { + if (typeof t === 'string') { + dataPaths.push(path.resolve(wf.uri.fsPath, t)); + } }); } } @@ -45,7 +49,12 @@ export function getCustomDataPathsFromAllExtensions(): string[] { for (const extension of extensions.all) { const contributes = extension.packageJSON && extension.packageJSON.contributes; - if (contributes && contributes.html && contributes.html.experimental.customData && Array.isArray(contributes.html.experimental.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 2d1da69fea6d0c210fed52bdc8aa9d06cdbeb2c7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 20:58:22 +0100 Subject: [PATCH 0850/1066] remove leftover console.log --- src/vs/editor/contrib/referenceSearch/referencesWidget.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index d238db4cd88..e04ca9e6c5d 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -433,7 +433,6 @@ export class ReferenceWidget extends PeekViewWidget { this._treeContainer.style.height = height; this._treeContainer.style.width = right; // forward - console.log(widthInPixel, this._sash.ratio, widthInPixel * (1 - this._sash.ratio)); this._tree.layout(heightInPixel, widthInPixel * (1 - this._sash.ratio)); this._preview.layout(); From 31feeba3f33dfb86ff481b77e91b9089475da8a1 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 30 Jan 2019 21:42:01 +0100 Subject: [PATCH 0851/1066] true tree focus and selection traits fixes #67220 --- src/vs/base/browser/ui/tree/abstractTree.ts | 161 ++++++++++++++++-- src/vs/base/browser/ui/tree/indexTreeModel.ts | 10 +- .../base/browser/ui/tree/objectTreeModel.ts | 4 +- src/vs/base/browser/ui/tree/tree.ts | 6 +- 4 files changed, 160 insertions(+), 21 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 1ff4a7f6dc0..036b4ddf543 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -8,7 +8,7 @@ import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/l import { IListOptions, List, IListStyles, mightProducePrintableCharacter, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; -import { Event, Relay, Emitter } from 'vs/base/common/event'; +import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; @@ -22,6 +22,7 @@ import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTr import { localize } from 'vs/nls'; import { disposableTimeout } from 'vs/base/common/async'; import { isMacintosh } from 'vs/base/common/platform'; +import { values } from 'vs/base/common/map'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -641,12 +642,135 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly autoExpandSingleChildren?: boolean; } +/** + * The trait concept needs to exist at the tree level, because collapsed + * tree nodes will not be known by the list. + */ +class Trait { + + private nodes: ITreeNode[] = []; + private elements: T[] | undefined; + + private _onDidChange = new Emitter>(); + readonly onDidChange = this._onDidChange.event; + + private _nodeSet: Set> | undefined; + private get nodeSet(): Set> { + if (!this._nodeSet) { + this._nodeSet = new Set(); + + for (const node of this.nodes) { + this._nodeSet.add(node); + } + } + + return this._nodeSet; + } + + set(nodes: ITreeNode[], browserEvent?: UIEvent): void { + this.nodes = [...nodes]; + this.elements = undefined; + this._nodeSet = undefined; + + const that = this; + this._onDidChange.fire({ get elements() { return that.get(); }, browserEvent }); + } + + get(): T[] { + if (!this.elements) { + this.elements = this.nodes.map(node => node.element); + } + + return [...this.elements]; + } + + has(node: ITreeNode): boolean { + return this.nodeSet.has(node); + } + + remove(nodes: ITreeNode[]): void { + const set = this.nodeSet; + + for (const node of nodes) { + set.delete(node); + } + + this.set(values(set)); + } +} + +/** + * We use this List subclass to restore selection and focus as nodes + * get rendered in the list, possibly due to a node expand() call. + */ +class TreeNodeList extends List> { + + constructor( + container: HTMLElement, + virtualDelegate: IListVirtualDelegate>, + renderers: IListRenderer[], + private focusTrait: Trait, + private selectionTrait: Trait, + options?: IListOptions> + ) { + super(container, virtualDelegate, renderers, options); + } + + splice(start: number, deleteCount: number, elements: ITreeNode[] = []): void { + super.splice(start, deleteCount, elements); + + if (elements.length === 0) { + return; + } + + const additionalFocus: number[] = []; + const additionalSelection: number[] = []; + + elements.forEach((node, index) => { + if (this.selectionTrait.has(node)) { + additionalFocus.push(start + index); + } + + if (this.selectionTrait.has(node)) { + additionalSelection.push(start + index); + } + }); + + if (additionalFocus.length > 0) { + super.setFocus([...super.getFocus(), ...additionalFocus]); + } + + if (additionalSelection.length > 0) { + super.setSelection([...super.getSelection(), ...additionalSelection]); + } + } + + setFocus(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setFocus(indexes, browserEvent); + + if (!fromAPI) { + this.focusTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } + + setSelection(indexes: number[], browserEvent?: UIEvent, fromAPI = false): void { + super.setSelection(indexes, browserEvent); + + if (!fromAPI) { + this.selectionTrait.set(indexes.map(i => this.element(i)), browserEvent); + } + } +} + export abstract class AbstractTree implements IDisposable { - private view: List>; + private view: TreeNodeList; private renderers: TreeRenderer[]; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; + private focus = new Trait(); + private selection = new Trait(); + private eventBufferer = new EventBufferer(); protected disposables: IDisposable[] = []; private _onDidUpdateOptions = new Emitter>(); @@ -654,8 +778,8 @@ export abstract class AbstractTree implements IDisposable 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); } + readonly onDidChangeFocus: Event> = this.eventBufferer.wrapEvent(this.focus.onDidChange); + readonly onDidChangeSelection: Event> = this.eventBufferer.wrapEvent(this.selection.onDidChange); get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } @@ -697,11 +821,18 @@ export abstract class AbstractTree implements IDisposable this.disposables.push(filter); } - this.view = new List(container, treeDelegate, this.renderers, asListOptions(() => this.model, _options)); + this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, asListOptions(() => this.model, _options)); this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; + this.model.onDidSplice(e => { + this.eventBufferer.bufferEvents(() => { + this.focus.remove(e.deletedNodes); + this.selection.remove(e.deletedNodes); + }); + }, null, this.disposables); + this.view.onTap(this.reactOnMouseClick, this, this.disposables); this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); @@ -853,18 +984,23 @@ export abstract class AbstractTree implements IDisposable } setSelection(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setSelection(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.selection.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setSelection(indexes, browserEvent, true); } getSelection(): T[] { - const nodes = this.view.getSelectedElements(); - return nodes.map(n => n.element); + return this.selection.get(); } setFocus(elements: TRef[], browserEvent?: UIEvent): void { - const indexes = elements.map(e => this.model.getListIndex(e)); - this.view.setFocus(indexes, browserEvent); + const nodes = elements.map(e => this.model.getNode(e)); + this.focus.set(nodes, browserEvent); + + const indexes = elements.map(e => this.model.getListIndex(e)).filter(i => i > -1); + this.view.setFocus(indexes, browserEvent, true); } focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { @@ -892,8 +1028,7 @@ export abstract class AbstractTree implements IDisposable } getFocus(): T[] { - const nodes = this.view.getFocusedElements(); - return nodes.map(n => n.element); + return this.focus.get(); } open(elements: TRef[]): void { diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index bea50064994..cc6555950aa 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; +import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; import { ISequence, Iterator } from 'vs/base/common/iterator'; @@ -61,7 +61,7 @@ export class IndexTreeModel, TFilterData = voi private filter?: ITreeFilter; private autoExpandSingleChildren: boolean; - private _onDidSplice = new Emitter(); + private _onDidSplice = new Emitter>(); readonly onDidSplice = this._onDidSplice.event; constructor(private list: ISpliceable>, rootElement: T, options: IIndexTreeModelOptions = {}) { @@ -127,7 +127,7 @@ export class IndexTreeModel, TFilterData = voi } const result = Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); - this._onDidSplice.fire(undefined); + this._onDidSplice.fire({ deletedNodes }); return result; } @@ -144,8 +144,8 @@ export class IndexTreeModel, TFilterData = voi } getListIndex(location: number[]): number { - const { listIndex, visible } = this.getTreeNodeWithListIndex(location); - return visible ? listIndex : -1; + const { listIndex, visible, revealed } = this.getTreeNodeWithListIndex(location); + return visible && revealed ? listIndex : -1; } getListRenderCount(location: number[]): number { diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index 467a7c76e2f..3169271298e 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -7,7 +7,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; -import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; export interface IObjectTreeModelOptions extends IIndexTreeModelOptions { readonly sorter?: ITreeSorter; @@ -21,7 +21,7 @@ export class ObjectTreeModel, TFilterData extends Non private nodes = new Map>(); private sorter?: ITreeSorter>; - readonly onDidSplice: Event; + readonly onDidSplice: Event>; readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 7cf94dbb023..4880293c269 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -95,10 +95,14 @@ export interface ICollapseStateChangeEvent { deep: boolean; } +export interface ITreeModelSpliceEvent { + deletedNodes: ITreeNode[]; +} + export interface ITreeModel { readonly rootRef: TRef; - readonly onDidSplice: Event; + readonly onDidSplice: Event>; readonly onDidChangeCollapseState: Event>; readonly onDidChangeRenderNodeCount: Event>; From b9a116e11b0c9150ff40f99a21a63ed09796c39c Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 30 Jan 2019 12:57:02 -0800 Subject: [PATCH 0852/1066] [HTML] Update language 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 3c95ec49a4e..ce5845b7345 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.10", + "vscode-html-languageservice": "^2.1.11-next.11", "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 1cf165efbca..056fdd468ad 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.10: - version "2.1.11-next.10" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.10.tgz#35c879e14fcf4d68870231eb5c46aa9a041b4aa1" - integrity sha512-HrQl0gIDLhTJB139nsMLhPkmG0YmweTgmd2MQlq7oki1+QFbIm3F/VyWpmGZR1aXGhRUmY58p1omg74NDy+4Lg== +vscode-html-languageservice@^2.1.11-next.11: + version "2.1.11-next.11" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-2.1.11-next.11.tgz#7dd00c07790d05751d5a40164b6983b0fd8cd02b" + integrity sha512-NOJRP1hCuEORd/Zs7yK4sQBRML6APDZ5zOMvbXRCVk5VyFx+KD0/uWjJWKnvgQMUONEMGD5emSFbSy8h80MtXw== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From 823e747e683e4d9b478521af3ea9e86ad3fe2d9f Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 30 Jan 2019 21:57:12 +0100 Subject: [PATCH 0853/1066] switch to theme when installing --- .../electron-browser/colorThemeStore.ts | 25 +++++-- .../electron-browser/fileIconThemeStore.ts | 23 ++++-- .../electron-browser/workbenchThemeService.ts | 72 +++++++++++++------ 3 files changed, 88 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 641dc73a13c..73a58b9b08d 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -46,22 +46,32 @@ const themesExtPoint = ExtensionsRegistry.registerExtensionPoint; + private readonly onDidChangeEmitter: Emitter; - public get onDidChange(): Event { return this.onDidChangeEmitter.event; } + public get onDidChange(): Event { return this.onDidChangeEmitter.event; } constructor(@IExtensionService private readonly extensionService: IExtensionService, defaultTheme: ColorThemeData) { this.extensionsColorThemes = [defaultTheme]; - this.onDidChangeEmitter = new Emitter(); + this.onDidChangeEmitter = new Emitter(); this.initialize(); } private initialize() { - themesExtPoint.setHandler((extensions) => { + themesExtPoint.setHandler((extensions, delta) => { + const previousIds = {}; + const added = []; + for (const theme of this.extensionsColorThemes) { + previousIds[theme.id] = true; + } this.extensionsColorThemes.length = 1; // remove all but the default theme for (let ext of extensions) { let extensionData = { @@ -72,7 +82,12 @@ export class ColorThemeStore { }; this.onThemes(ext.description.extensionLocation, extensionData, ext.value, ext.collector); } - this.onDidChangeEmitter.fire(this.extensionsColorThemes); + for (const theme of this.extensionsColorThemes) { + if (!previousIds[theme.id]) { + added.push(theme); + } + } + this.onDidChangeEmitter.fire({ themes: this.extensionsColorThemes, added }); }); } diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 39d83bf5ef7..45d43d7d0d5 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -42,21 +42,31 @@ const iconThemeExtPoint = ExtensionsRegistry.registerExtensionPoint; + private readonly onDidChangeEmitter: Emitter; - public get onDidChange(): Event { return this.onDidChangeEmitter.event; } + public get onDidChange(): Event { return this.onDidChangeEmitter.event; } constructor(@IExtensionService private readonly extensionService: IExtensionService) { this.knownIconThemes = []; - this.onDidChangeEmitter = new Emitter(); + this.onDidChangeEmitter = new Emitter(); this.initialize(); } private initialize() { iconThemeExtPoint.setHandler((extensions) => { + const previousIds = {}; + const added = []; + for (const theme of this.knownIconThemes) { + previousIds[theme.id] = true; + } this.knownIconThemes.length = 0; for (let ext of extensions) { let extensionData = { @@ -67,7 +77,12 @@ export class FileIconThemeStore { }; this.onIconThemes(ext.description.extensionLocation, extensionData, ext.value, ext.collector); } - this.onDidChangeEmitter.fire(this.knownIconThemes); + for (const theme of this.knownIconThemes) { + if (!previousIds[theme.id]) { + added.push(theme); + } + } + this.onDidChangeEmitter.fire({ themes: this.knownIconThemes, added }); }); } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index fb1ffc5a0ab..f53629370b3 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -145,14 +145,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { this.installConfigurationListener(); }); + let prevColorId: string | undefined = undefined; + // update settings schema setting for theme specific settings - this.colorThemeStore.onDidChange(themes => { + this.colorThemeStore.onDidChange(async event => { const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} }; const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false }; const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false }; - for (let t of themes) { + for (let t of event.themes) { // add a enum value to the 'workbench.colorTheme` setting colorThemeSettingSchema.enum!.push(t.settingsId); colorThemeSettingSchema.enumDescriptions!.push(t.description || ''); @@ -169,28 +171,42 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); if (this.currentColorTheme.isLoaded) { - this.colorThemeStore.findThemeData(this.currentColorTheme.id).then(theme => { - if (!theme) { - this.setColorTheme(DEFAULT_THEME_ID, undefined); - } else { - this.restoreColorTheme(); + if (event.added.length > 0 && event.added.length < event.themes.length) { + prevColorId = this.currentColorTheme.id; + this.setColorTheme(event.added[0].id, 'auto'); + } else { + const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); + if (!themeData) { + if (prevColorId && await this.colorThemeStore.findThemeData(prevColorId)) { + this.setColorTheme(prevColorId, 'auto'); + } else { + this.setColorTheme(DEFAULT_THEME_ID, 'auto'); + } } - }); + } } }); - this.iconThemeStore.onDidChange(themes => { - iconThemeSettingSchema.enum = [null, ...themes.map(t => t.settingsId)]; - iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...themes.map(t => t.description || '')]; + + let prevFileIconId: string | undefined = undefined; + this.iconThemeStore.onDidChange(async event => { + iconThemeSettingSchema.enum = [null, ...event.themes.map(t => t.settingsId)]; + iconThemeSettingSchema.enumDescriptions = [iconThemeSettingSchema.enumDescriptions![0], ...event.themes.map(t => t.description || '')]; configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); if (this.currentIconTheme.isLoaded) { - this.iconThemeStore.findThemeData(this.currentIconTheme.id).then(theme => { + if (event.added.length > 0 && event.added.length < event.themes.length) { + prevFileIconId = this.currentIconTheme.id; + this.setFileIconTheme(event.added[0].id, 'auto'); + } else { + const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id); if (!theme) { - this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, undefined); - } else { - this.restoreFileIconTheme(); + if (prevFileIconId && await this.iconThemeStore.findThemeData(prevColorId)) { + this.setFileIconTheme(prevFileIconId, 'auto'); + } else { + this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); + } } - }); + } } }); } @@ -301,7 +317,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.getColorTheme(); } - public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise { + public setColorTheme(themeId: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!themeId) { return Promise.resolve(null); } @@ -360,7 +376,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { _applyRules(cssRules.join('\n'), colorThemeRulesClassName); } - private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined, silent = false): Promise { + private applyTheme(newTheme: ColorThemeData, settingsTarget: ConfigurationTarget | undefined | 'auto', silent = false): Promise { if (this.container) { if (this.currentColorTheme) { removeClasses(this.container, this.currentColorTheme.id); @@ -399,7 +415,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.writeColorThemeConfiguration(settingsTarget); } - private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined): Promise { + private writeColorThemeConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { return this.configurationWriter.writeConfiguration(COLOR_THEME_SETTING, this.currentColorTheme.settingsId, settingsTarget).then(_ => this.currentColorTheme); } @@ -444,7 +460,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return this.currentIconTheme; } - public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined): Promise { + public setFileIconTheme(iconTheme: string | undefined, settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { iconTheme = iconTheme || ''; if (iconTheme === this.currentIconTheme.id && this.currentIconTheme.isLoaded) { return this.writeFileIconConfiguration(settingsTarget); @@ -506,7 +522,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } - private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined): Promise { + private writeFileIconConfiguration(settingsTarget: ConfigurationTarget | undefined | 'auto'): Promise { if (!types.isUndefinedOrNull(settingsTarget)) { return this.configurationWriter.writeConfiguration(ICON_THEME_SETTING, this.currentIconTheme.settingsId, settingsTarget).then(_ => this.currentIconTheme); } @@ -559,8 +575,18 @@ class ConfigurationWriter { constructor(@IConfigurationService private readonly configurationService: IConfigurationService) { } - public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget): Promise { + public writeConfiguration(key: string, value: any, settingsTarget: ConfigurationTarget | 'auto'): Promise { let settings = this.configurationService.inspect(key); + if (settingsTarget === 'auto') { + if (!types.isUndefined(settings.workspaceFolder)) { + settingsTarget = ConfigurationTarget.WORKSPACE_FOLDER; + } else if (!types.isUndefined(settings.workspace)) { + settingsTarget = ConfigurationTarget.WORKSPACE; + } else { + settingsTarget = ConfigurationTarget.USER; + } + } + if (settingsTarget === ConfigurationTarget.USER) { if (value === settings.user) { return Promise.resolve(undefined); // nothing to do @@ -570,7 +596,7 @@ class ConfigurationWriter { } value = undefined; // remove configuration from user settings } - } else if (settingsTarget === ConfigurationTarget.WORKSPACE) { + } else if (settingsTarget === ConfigurationTarget.WORKSPACE || settingsTarget === ConfigurationTarget.WORKSPACE_FOLDER) { if (value === settings.value) { return Promise.resolve(undefined); // nothing to do } From 10c3fcb4fc6d8e6ddc5a995763ce188d9656f364 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 12:58:01 -0800 Subject: [PATCH 0854/1066] Fixes #67430 --- .../typescript-language-features/src/features/completions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index c0020fdfef0..d5822dede34 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -457,7 +457,7 @@ class TypeScriptCompletionItemProvider implements vscode.CompletionItemProvider }; const response = await this.client.interruptGetErr(() => this.client.execute('completionEntryDetails', args, token)); - if (response.type !== 'response' || !response.body) { + if (response.type !== 'response' || !response.body || !response.body.length) { return item; } From 3e255d98cf6598fbc923a45da83e9b9bdd254355 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 30 Jan 2019 22:16:35 +0100 Subject: [PATCH 0855/1066] fix null checks --- .../services/themes/electron-browser/colorThemeStore.ts | 4 ++-- .../services/themes/electron-browser/fileIconThemeStore.ts | 4 ++-- .../services/themes/electron-browser/workbenchThemeService.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 73a58b9b08d..0b724501115 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -67,8 +67,8 @@ export class ColorThemeStore { private initialize() { themesExtPoint.setHandler((extensions, delta) => { - const previousIds = {}; - const added = []; + const previousIds: { [key: string]: boolean } = {}; + const added: ColorThemeData[] = []; for (const theme of this.extensionsColorThemes) { previousIds[theme.id] = true; } diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 45d43d7d0d5..437387c81f4 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -62,8 +62,8 @@ export class FileIconThemeStore { private initialize() { iconThemeExtPoint.setHandler((extensions) => { - const previousIds = {}; - const added = []; + const previousIds: { [key: string]: boolean } = {}; + const added: FileIconThemeData[] = []; for (const theme of this.knownIconThemes) { previousIds[theme.id] = true; } diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index f53629370b3..a64c0f8be21 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -200,7 +200,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { } else { const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id); if (!theme) { - if (prevFileIconId && await this.iconThemeStore.findThemeData(prevColorId)) { + if (prevFileIconId && await this.iconThemeStore.findThemeData(prevFileIconId)) { this.setFileIconTheme(prevFileIconId, 'auto'); } else { this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); From 4833debfe0c07d2631a180250ab7821f6baa47e8 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Wed, 30 Jan 2019 14:27:40 -0800 Subject: [PATCH 0856/1066] Update css grammar. Fix #66775 --- extensions/css/syntaxes/css.tmLanguage.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index 488c8f12c30..a45463c8b9b 100644 --- a/extensions/css/syntaxes/css.tmLanguage.json +++ b/extensions/css/syntaxes/css.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/octref/language-css/commit/7e06c88b89218fe6e8eba77fb674152f1cea0b10", + "version": "https://github.com/octref/language-css/commit/6d3a2d01dd67ef062030f4520dd42a5424330a3b", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -1397,7 +1397,7 @@ "property-names": { "patterns": [ { - "match": "(?xi) (? Date: Wed, 30 Jan 2019 14:51:58 -0800 Subject: [PATCH 0857/1066] Fixes keybinding `args` contribution type #31372 --- .../services/keybinding/electron-browser/keybindingService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts index 24399d5b830..817b087ec8e 100644 --- a/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/electron-browser/keybindingService.ts @@ -209,8 +209,7 @@ let keybindingType: IJSONSchema = { type: 'string' }, args: { - 'description': nls.localize('vscode.extension.contributes.keybindings.args', "Arguments to pass to the command to execute."), - type: 'any' + description: nls.localize('vscode.extension.contributes.keybindings.args', "Arguments to pass to the command to execute.") }, key: { description: nls.localize('vscode.extension.contributes.keybindings.key', 'Key or key sequence (separate keys with plus-sign and sequences with space, e.g Ctrl+O and Ctrl+L L for a chord).'), From 1fb26f9b5791f612e322589383fb99fbcb005e7b Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Jan 2019 14:53:00 -0800 Subject: [PATCH 0858/1066] make editor area return more intuitive --- src/vs/workbench/browser/parts/editor/editorPart.ts | 4 ++++ src/vs/workbench/electron-browser/workbench.ts | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index d64cb6ecce4..e27bd5db5de 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -110,6 +110,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor private _onDidPreferredSizeChange: Emitter = this._register(new Emitter()); get onDidPreferredSizeChange(): Event { return this._onDidPreferredSizeChange.event; } + private _onDidActivateGroup: Emitter = this._register(new Emitter()); + get onDidActivateGroup(): Event { return this._onDidActivateGroup.event; } + //#endregion private dimension: Dimension; @@ -319,6 +322,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor const groupView = this.assertGroupView(group); this.doSetGroupActive(groupView); + this._onDidActivateGroup.fire(groupView); return groupView; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index f20aff0ed3b..9a61a7653a3 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -502,6 +502,9 @@ export class Workbench extends Disposable implements IPartService { // Listen to visible editor changes this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); + // Listen to editor group activations when editor is hidden + this._register(this.editorPart.onDidActivateGroup(() => { if (this.editorHidden) { this.setEditorHidden(false); } })); + // Listen to editor closing (if we run with --wait) const filesToWait = this.workbenchParams.configuration.filesToWait; if (filesToWait) { @@ -1272,6 +1275,8 @@ export class Workbench extends Disposable implements IPartService { return !this.statusBarHidden; case Parts.ACTIVITYBAR_PART: return !this.activityBarHidden; + case Parts.EDITOR_PART: + return this.workbenchGrid instanceof Grid ? !this.editorHidden : true; } return true; // any other part cannot be hidden From 498eb67b0c2d7bd29f8a724d619675a18a988687 Mon Sep 17 00:00:00 2001 From: Rachel Macfarlane Date: Wed, 30 Jan 2019 14:56:04 -0800 Subject: [PATCH 0859/1066] Scope reactions css for comments --- .../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 b8cf5584e59..040faaee75b 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -86,7 +86,7 @@ } -.monaco-editor .review-widget .body .monaco-text-button { +.monaco-editor .review-widget .body .comment-reactions .monaco-text-button { margin: 0 7px 0 0; width: 30px; background-color: transparent; From 0d7a9b186034af672637df3faf6c6b735c674283 Mon Sep 17 00:00:00 2001 From: kieferrm Date: Wed, 30 Jan 2019 15:59:04 -0800 Subject: [PATCH 0860/1066] update distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4f78caf443e..eea0c327466 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.31.0", - "distro": "455b23e66edb0fb17a1d7f5764ff35eec7c1b2d1", + "distro": "cb7599e6323871114d034681abb30ed5e2066d3c", "author": { "name": "Microsoft Corporation" }, From 536b8cc95af732c010567392ae2db55dc92c4758 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Jan 2019 16:03:20 -0800 Subject: [PATCH 0861/1066] fix moving panel while hidden in grid layout fixes #67370 --- 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 9a61a7653a3..9ed3fc19dba 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1804,9 +1804,9 @@ export class Workbench extends Disposable implements IPartService { if (this.workbenchGrid instanceof Grid) { if (!wasHidden) { this.saveLastPanelDimension(); - this.workbenchGrid.removeView(this.panelPartView); } + this.workbenchGrid.removeView(this.panelPartView); this.layout(); } else { this.workbenchGrid.layout(); From f28a9f918d8b056a05aeb2cd2800058c71786bc9 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 30 Jan 2019 16:07:52 -0800 Subject: [PATCH 0862/1066] fix moving sidebar while hidden fixes #67370 --- src/vs/workbench/electron-browser/workbench.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index 9ed3fc19dba..f222c743e7d 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -1738,12 +1738,10 @@ export class Workbench extends Disposable implements IPartService { if (!wasHidden) { this.uiState.lastSidebarDimension = this.workbenchGrid.getViewSize(this.sidebarPartView); - this.workbenchGrid.removeView(this.sidebarPartView); } - if (!this.activityBarHidden) { - this.workbenchGrid.removeView(this.activitybarPartView); - } + this.workbenchGrid.removeView(this.sidebarPartView); + this.workbenchGrid.removeView(this.activitybarPartView); if (!this.panelHidden && this.panelPosition === Position.BOTTOM) { this.workbenchGrid.removeView(this.panelPartView); From d6a9c8c6144d2e58dfb6570981763163cbe5f04a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 16:00:25 -0800 Subject: [PATCH 0863/1066] Recomend new tslint extension on welcome page --- .../parts/welcome/page/electron-browser/welcomePage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3e7bb14b1af..9a60c85c0e6 100644 --- a/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts +++ b/src/vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts @@ -146,7 +146,7 @@ interface ExtensionSuggestion { const extensionPacks: ExtensionSuggestion[] = [ { name: localize('welcomePage.javaScript', "JavaScript"), id: 'dbaeumer.vscode-eslint' }, - { name: localize('welcomePage.typeScript', "TypeScript"), id: 'eg2.tslint' }, + { name: localize('welcomePage.typeScript', "TypeScript"), id: 'ms-vscode.vscode-typescript-tslint-plugin' }, { name: localize('welcomePage.python', "Python"), id: 'ms-python.python' }, // { name: localize('welcomePage.go', "Go"), id: 'lukehoban.go' }, { name: localize('welcomePage.php', "PHP"), id: 'felixfbecker.php-pack' }, From 7a53ca7deca120e8e60a4fe4a4b429272d2da9b8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 16:10:49 -0800 Subject: [PATCH 0864/1066] Update distro version --- package.json | 2 +- .../electron-browser/extensionsTipsService.test.ts | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index eea0c327466..5653c44fcae 100644 --- a/package.json +++ b/package.json @@ -150,4 +150,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" } -} +} \ No newline at end of file diff --git a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts index fbe007ccbed..64a23326120 100644 --- a/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/parts/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -370,7 +370,7 @@ suite('ExtensionsTipsService Test', () => { test('ExtensionTipsService: No Recommendations of globally ignored recommendations', () => { const storageGetterStub = (a, _, c) => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "eg2.tslint"]'; + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; const ignoredRecommendations = '["ms-vscode.csharp", "mockpublisher2.mockextension2"]'; // ignore a stored recommendation and a workspace recommendation. if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } if (a === 'extensionsAssistant/ignored_recommendations') { return ignoredRecommendations; } @@ -508,7 +508,7 @@ suite('ExtensionsTipsService Test', () => { }); test('ExtensionTipsService: Get file based recommendations from storage (old format)', () => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "eg2.tslint"]'; + const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; instantiationService.stub(IStorageService, { get: (a, b, c) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c }); return setUpFolderWorkspace('myFolder', []).then(() => { @@ -518,7 +518,7 @@ suite('ExtensionsTipsService Test', () => { assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'eg2.tslint')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips }); }); }); @@ -527,7 +527,7 @@ suite('ExtensionsTipsService Test', () => { const milliSecondsInADay = 1000 * 60 * 60 * 24; const now = Date.now(); const tenDaysOld = 10 * milliSecondsInADay; - const storedRecommendations = `{"ms-vscode.csharp": ${now}, "ms-python.python": ${now}, "eg2.tslint": ${now}, "lukehoban.Go": ${tenDaysOld}}`; + const storedRecommendations = `{"ms-vscode.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; instantiationService.stub(IStorageService, { get: (a, b, c) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c }); return setUpFolderWorkspace('myFolder', []).then(() => { @@ -537,7 +537,7 @@ suite('ExtensionsTipsService Test', () => { assert.equal(recommendations.length, 2); assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips - assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'eg2.tslint')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips + assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week }); }); From 9a766eaa38990f47f40a76b7ba2511dbcf74c0c4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Jan 2019 18:50:41 -0800 Subject: [PATCH 0865/1066] Make sure we update tag closing registration when the document's language changes Fixes #67613 --- .../src/features/tagClosing.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/typescript-language-features/src/features/tagClosing.ts b/extensions/typescript-language-features/src/features/tagClosing.ts index e4df1264ccf..34ddd2c0285 100644 --- a/extensions/typescript-language-features/src/features/tagClosing.ts +++ b/extensions/typescript-language-features/src/features/tagClosing.ts @@ -144,6 +144,7 @@ export class ActiveDocumentDependentRegistration extends Disposable { super(); this._registration = this._register(new ConditionalRegistration(register)); vscode.window.onDidChangeActiveTextEditor(this.update, this, this._disposables); + vscode.workspace.onDidOpenTextDocument(this.onDidOpenDocument, this, this._disposables); this.update(); } @@ -152,6 +153,13 @@ export class ActiveDocumentDependentRegistration extends Disposable { const enabled = !!(editor && vscode.languages.match(this.selector, editor.document)); this._registration.update(enabled); } + + private onDidOpenDocument(openedDocument: vscode.TextDocument) { + if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === openedDocument) { + // The active document's language may have changed + this.update(); + } + } } export function register( From 9b1f263a88f91170fff51ede1b030b165ce53cd4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Jan 2019 20:45:02 -0800 Subject: [PATCH 0866/1066] Fix #67587 --- .../electron-browser/media/settingsEditor2.css | 13 +++++++++++-- .../electron-browser/settingsEditor2.ts | 17 ++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) 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 a29751e89bd..0674380596b 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -99,7 +99,7 @@ position: relative; } -.settings-editor > .settings-body > .no-results { +.settings-editor > .settings-body > .no-results-message { display: none; max-width: 1000px; margin: auto; @@ -109,7 +109,16 @@ box-sizing: border-box; } -.settings-editor > .settings-body > .no-results a.prominent { +.settings-editor.no-results > .settings-body .settings-toc-container, +.settings-editor.no-results > .settings-body .settings-tree-container { + display: none; +} + +.settings-editor.no-results > .settings-body > .no-results-message { + display: block; +} + +.settings-editor > .settings-body > .no-results-message a.prominent { text-decoration: underline; } diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 30d99008bc7..b0d31cad7b7 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -455,7 +455,7 @@ export class SettingsEditor2 extends BaseEditor { private createBody(parent: HTMLElement): void { const bodyContainer = DOM.append(parent, $('.settings-body')); - this.noResultsMessage = DOM.append(bodyContainer, $('.no-results')); + this.noResultsMessage = DOM.append(bodyContainer, $('.no-results-message')); this.noResultsMessage.innerText = localize('noResults', "No Settings Found"); @@ -950,6 +950,8 @@ export class SettingsEditor2 extends BaseEditor { } } + this.renderResultCountMessages(); + if (key) { const elements = this.currentSettingsModel.getElementsByName(key); if (elements && elements.length) { @@ -1034,13 +1036,13 @@ export class SettingsEditor2 extends BaseEditor { // Added a filter model this.tocTree.setSelection([]); this.tocTree.expandAll(); - this.refreshTree(); this.renderResultCountMessages(); + this.refreshTree(); } else { // Leaving search mode this.tocTree.collapseAll(); - this.refreshTree(); this.renderResultCountMessages(); + this.refreshTree(); } this.refreshTOCTree(); @@ -1147,9 +1149,7 @@ export class SettingsEditor2 extends BaseEditor { return Promise.all([ this.filterOrSearchPreferences(query, SearchResultIdx.Remote, remoteSearchProvider, token), this.filterOrSearchPreferences(query, SearchResultIdx.NewExtensions, newExtSearchProvider, token) - ]).then(() => { - this.renderResultCountMessages(); - }); + ]).then(() => { }); } private filterOrSearchPreferences(query: string, type: SearchResultIdx, searchProvider: ISearchProvider, token?: CancellationToken): Promise { @@ -1164,7 +1164,6 @@ export class SettingsEditor2 extends BaseEditor { this.searchResultModel.setResult(type, result); this.tocTreeModel.currentSearchModel = this.searchResultModel; this.onSearchModeToggled(); - this.refreshTree(); } else { this.searchResultModel.setResult(type, result); this.tocTreeModel.update(); @@ -1174,7 +1173,7 @@ export class SettingsEditor2 extends BaseEditor { this.viewState.filterToCategory = null; this.tocTree.expandAll(); - return this.renderTree().then(() => result); + return this.renderTree(undefined, true).then(() => result); }); } @@ -1192,7 +1191,7 @@ export class SettingsEditor2 extends BaseEditor { } this.countElement.style.display = 'block'; - this.noResultsMessage.style.display = count === 0 ? 'block' : 'none'; + DOM.toggleClass(this.rootElement, 'no-results', count === 0); this.clearFilterLinkContainer.style.display = this.viewState.tagFilters && this.viewState.tagFilters.size > 0 ? 'initial' : 'none'; From 7f136b44cff0cd3486ac88b43598b8c72b025d16 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 30 Jan 2019 21:14:46 -0800 Subject: [PATCH 0867/1066] Fix bad highlight in inactive toc tree --- src/vs/workbench/parts/preferences/browser/tocTree.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index b3459279b3c..ac87ea537d6 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -5,6 +5,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { DefaultStyleController } from 'vs/base/browser/ui/list/listWidget'; import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Iterator } from 'vs/base/common/iterator'; @@ -157,6 +158,7 @@ export class TOCTree extends ObjectTree { ) { // test open mode + const treeClass = 'settings-toc-tree'; const filter = instantiationService.createInstance(SettingsTreeFilter, viewState); const options: IObjectTreeOptions = { filter, @@ -165,7 +167,8 @@ export class TOCTree extends ObjectTree { getId(e) { return e.id; } - } + }, + styleController: new DefaultStyleController(DOM.createStyleSheet(container), treeClass) }; super(container, @@ -173,7 +176,6 @@ export class TOCTree extends ObjectTree { [new TOCRenderer()], options); - const treeClass = 'settings-toc-tree'; this.getHTMLElement().classList.add(treeClass); this.disposables.push(attachStyler(themeService, { @@ -187,6 +189,8 @@ export class TOCTree extends ObjectTree { listHoverBackground: editorBackground, listInactiveSelectionBackground: editorBackground, listInactiveSelectionForeground: settingsHeaderForeground, + listInactiveFocusBackground: editorBackground, + listInactiveFocusOutline: editorBackground }, colors => { this.style(colors); })); From 07bc7cb9dff13a7742ab69d9b2fd46760f9f1261 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Wed, 30 Jan 2019 22:24:07 -0800 Subject: [PATCH 0868/1066] Fix terminal reflow problems Fixes #67439 Fixes #67359 Fixes #67364 --- package.json | 2 +- .../electron-browser/terminalInstance.ts | 59 ++++++++++--------- yarn.lock | 8 +-- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 5653c44fcae..781fc7bde22 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.11.0-beta1", + "vscode-xterm": "3.11.0-beta2", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index c0c72063d10..b19e38fcae5 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -3,42 +3,42 @@ * 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'; -import * as dom from 'vs/base/browser/dom'; -import * as paths from 'vs/base/common/paths'; +import { execFile } from 'child_process'; import * as os from 'os'; import * as path from 'path'; -import { Event, Emitter } from 'vs/base/common/event'; -import { debounce } from 'vs/base/common/decorators'; -import { WindowsShellHelper } from 'vs/workbench/parts/terminal/node/windowsShellHelper'; -import { Terminal as XTermTerminal, ISearchOptions } from 'vscode-xterm'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ITerminalInstance, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, IShellLaunchConfig, ITerminalProcessManager, ProcessState, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ITerminalDimensions } from 'vs/workbench/parts/terminal/common/terminal'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import * as browser from 'vs/base/browser/browser'; +import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { debounce } from 'vs/base/common/decorators'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import * as lifecycle from 'vs/base/common/lifecycle'; +import * as paths from 'vs/base/common/paths'; +import * as platform from 'vs/base/common/platform'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; +import * as nls from 'vs/nls'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { activeContrastBorder, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry'; +import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/terminalWidgetManager'; +import { IShellLaunchConfig, ITerminalDimensions, ITerminalInstance, ITerminalProcessManager, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, NEVER_MEASURE_RENDER_TIME_STORAGE_KEY, ProcessState, TERMINAL_PANEL_ID } from 'vs/workbench/parts/terminal/common/terminal'; +import { ansiColorIdentifiers, TERMINAL_BACKGROUND_COLOR, TERMINAL_CURSOR_BACKGROUND_COLOR, TERMINAL_CURSOR_FOREGROUND_COLOR, TERMINAL_FOREGROUND_COLOR, TERMINAL_SELECTION_BACKGROUND_COLOR } from 'vs/workbench/parts/terminal/common/terminalColorRegistry'; +import { TERMINAL_COMMAND_ID } from 'vs/workbench/parts/terminal/common/terminalCommands'; import { TerminalConfigHelper } from 'vs/workbench/parts/terminal/electron-browser/terminalConfigHelper'; import { TerminalLinkHandler } from 'vs/workbench/parts/terminal/electron-browser/terminalLinkHandler'; -import { TerminalWidgetManager } from 'vs/workbench/parts/terminal/browser/terminalWidgetManager'; -import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; -import { scrollbarSliderBackground, scrollbarSliderHoverBackground, scrollbarSliderActiveBackground, activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -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 { 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'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -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'; +import { TerminalCommandTracker } from 'vs/workbench/parts/terminal/node/terminalCommandTracker'; +import { WindowsShellHelper } from 'vs/workbench/parts/terminal/node/windowsShellHelper'; +import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { ISearchOptions, Terminal as XTermTerminal } from 'vscode-xterm'; // How long in milliseconds should an average frame take to render for a notification to appear // which suggests the fallback DOM-based renderer @@ -1185,6 +1185,7 @@ export class TerminalInstance implements ITerminalInstance { this._resize(); } + @debounce(50) private _resize(): void { let cols = this._cols; let rows = this._rows; diff --git a/yarn.lock b/yarn.lock index 82332aa7d79..42352c9383a 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.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== +vscode-xterm@3.11.0-beta2: + version "3.11.0-beta2" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.11.0-beta2.tgz#fe5295c835fcff00fee55cbb1d5a4bf537d105a2" + integrity sha512-QG9ALgfH4+YUblN1G/AAdf40MJPOeb0p2u2k9MwcIvaJGQmk5Pp2KZUiQbO+ycfeuisTTVtqjceG7MutIEVZSQ== vso-node-api@6.1.2-preview: version "6.1.2-preview" From d5e2c72eb08fce7510fd6e23265f0ab22912725b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 09:27:05 +0100 Subject: [PATCH 0869/1066] fixes #67492 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- .../workbench/browser/actions/listCommands.ts | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 036b4ddf543..de9e3c1eb1c 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1115,7 +1115,7 @@ export abstract class AbstractTree implements IDisposable const didChange = this.model.setCollapsed(location, false); if (!didChange) { - if (node.children.length === 0) { + if (!node.children.some(child => child.visible)) { return; } diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index e5ee5abd4e3..9b113ef082b 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -310,6 +310,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Tree only if (focused && !(focused instanceof List || focused instanceof PagedList)) { if (focused instanceof ObjectTree || focused instanceof DataTree) { + // TODO@Joao: instead of doing this here, just delegate to a tree method const tree = focused; const focusedElements = tree.getFocus(); @@ -323,12 +324,17 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const child = tree.getFirstElementChild(focus); if (child) { - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setFocus([child], fakeKeyboardEvent); - tree.reveal(child); + const node = tree.getNode(child); + + if (node.visible) { + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); + tree.reveal(child); + } } } } else if (focused instanceof AsyncDataTree) { + // TODO@Joao: instead of doing this here, just delegate to a tree method const tree = focused; const focusedElements = tree.getFocus(); @@ -342,9 +348,13 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ const child = tree.getFirstElementChild(focus); if (child) { - const fakeKeyboardEvent = new KeyboardEvent('keydown'); - tree.setFocus([child], fakeKeyboardEvent); - tree.reveal(child); + const node = tree.getNode(child); + + if (node.visible) { + const fakeKeyboardEvent = new KeyboardEvent('keydown'); + tree.setFocus([child], fakeKeyboardEvent); + tree.reveal(child); + } } } }); From 66b8820e56a23a38c4f4b21ee47c71fc15a8736f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 09:47:29 +0100 Subject: [PATCH 0870/1066] improve tree trait behaviour fixes #67595 --- src/vs/base/browser/ui/tree/abstractTree.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index de9e3c1eb1c..9521a95a882 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -689,12 +689,17 @@ class Trait { } remove(nodes: ITreeNode[]): void { - const set = this.nodeSet; - - for (const node of nodes) { - set.delete(node); + if (nodes.length === 0) { + return; } + const set = this.nodeSet; + const visit = (node: ITreeNode) => { + set.delete(node); + node.children.forEach(visit); + }; + + nodes.forEach(visit); this.set(values(set)); } } From fcefb67a2a7c2a848eba8f4c9381c6236134825e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 09:56:37 +0100 Subject: [PATCH 0871/1066] fixes #67627 --- src/vs/base/browser/ui/tree/abstractTree.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9521a95a882..d5019da6031 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -493,7 +493,7 @@ class TypeFilterController implements IDisposable { const element = focus[0]; if (this.tree.getRelativeTop(element) === null) { - this.tree.reveal(focus[0], 0.5); + this.tree.reveal(element, 0.5); } } @@ -1043,6 +1043,11 @@ export abstract class AbstractTree implements IDisposable reveal(location: TRef, relativeTop?: number): void { const index = this.model.getListIndex(location); + + if (index === -1) { + return null; + } + this.view.reveal(index, relativeTop); } @@ -1052,6 +1057,11 @@ export abstract class AbstractTree implements IDisposable */ getRelativeTop(location: TRef): number | null { const index = this.model.getListIndex(location); + + if (index === -1) { + return null; + } + return this.view.getRelativeTop(index); } From c8e5503a4c67926160f0b8b418177919f66691d0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 09:58:29 +0100 Subject: [PATCH 0872/1066] Fix #67629 --- .../parts/extensions/electron-browser/extensionsActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 675cf034634..c24ff4e6f98 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -569,7 +569,7 @@ export class InstallAnotherVersionAction extends ExtensionAction { } update(): void { - this.enabled = this.extension && !!this.extension.gallery; + this.enabled = this.extension && this.extension.type === ExtensionType.User && !!this.extension.gallery; } run(): Promise { @@ -2483,7 +2483,7 @@ export class InstallSpecificVersionOfExtensionAction extends Action { } private isEnabled(extension: IExtension): boolean { - return !!extension.gallery && (extension.enablementState === EnablementState.Enabled || extension.enablementState === EnablementState.WorkspaceEnabled); + return extension.type === ExtensionType.User && !!extension.gallery && (extension.enablementState === EnablementState.Enabled || extension.enablementState === EnablementState.WorkspaceEnabled); } private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> { From 4710cd8c3d34acea67ab167a926f3318aca9aa12 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 10:09:26 +0100 Subject: [PATCH 0873/1066] Revert "Fix #67629" This reverts commit c8e5503a4c67926160f0b8b418177919f66691d0. --- .../parts/extensions/electron-browser/extensionsActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index c24ff4e6f98..675cf034634 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -569,7 +569,7 @@ export class InstallAnotherVersionAction extends ExtensionAction { } update(): void { - this.enabled = this.extension && this.extension.type === ExtensionType.User && !!this.extension.gallery; + this.enabled = this.extension && !!this.extension.gallery; } run(): Promise { @@ -2483,7 +2483,7 @@ export class InstallSpecificVersionOfExtensionAction extends Action { } private isEnabled(extension: IExtension): boolean { - return extension.type === ExtensionType.User && !!extension.gallery && (extension.enablementState === EnablementState.Enabled || extension.enablementState === EnablementState.WorkspaceEnabled); + return !!extension.gallery && (extension.enablementState === EnablementState.Enabled || extension.enablementState === EnablementState.WorkspaceEnabled); } private async getExtensionEntries(): Promise<(IQuickPickItem & { extension: IExtension, versions: IGalleryExtensionVersion[] })[]> { From a6d5a74a05e99ea18f73142452fc4c41c240d474 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 10:26:22 +0100 Subject: [PATCH 0874/1066] Fix #67604 --- .../parts/extensions/electron-browser/extensionsActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 675cf034634..de2c77c9b6a 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -2062,7 +2062,7 @@ export class StatusLabelAction extends Action implements IExtensionContainer { private _extension: IExtension; get extension(): IExtension { return this._extension; } set extension(extension: IExtension) { - if (!(this._extension && areSameExtensions(this._extension.identifier, extension.identifier))) { + if (!(this._extension && extension && areSameExtensions(this._extension.identifier, extension.identifier))) { // Different extension. Reset this.status = null; this.enablementState = null; From a538e27ef1085a9162906f2e84e2803a738911e3 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 09:59:53 +0100 Subject: [PATCH 0875/1066] fixes #67591 --- src/vs/workbench/browser/actions/listCommands.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 9b113ef082b..5fbd81fae8e 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -643,12 +643,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } 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) { @@ -664,6 +658,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ // Add the whole scope subtree to the new selection visit(tree.getNode(scope)); + // If the scope isn't the tree root, it should be part of the new selection + if (scope && selection.length === newSelection.length) { + newSelection.unshift(scope); + } + const fakeKeyboardEvent = new KeyboardEvent('keydown'); tree.setSelection(newSelection, fakeKeyboardEvent); } From f1c66a1cbe52b6947ba88f32233c6a2dfe4fa0f9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 10:38:17 +0100 Subject: [PATCH 0876/1066] strict null --- 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 d5019da6031..56ea99c13f0 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1045,7 +1045,7 @@ export abstract class AbstractTree implements IDisposable const index = this.model.getListIndex(location); if (index === -1) { - return null; + return; } this.view.reveal(index, relativeTop); From 3b28d9eb1795564a13c23b93dde7e4ee0f1b225e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 31 Jan 2019 10:45:17 +0100 Subject: [PATCH 0877/1066] Fixes #67093: Require a reload when encountering 3rd party extension points --- .../extensions/electron-browser/extensionService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 8d8af492103..36eafb01844 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -265,7 +265,13 @@ export class ExtensionService extends Disposable implements IExtensionService { for (let extPointName in extension.contributes) { if (hasOwnProperty.call(extension.contributes, extPointName)) { const extPoint = extensionPoints[extPointName]; - if (extPoint && !extPoint.isDynamic) { + if (extPoint) { + if (!extPoint.isDynamic) { + return false; + } + } else { + // This extension has a 3rd party (unknown) extension point + // ===> require a reload for now... return false; } } From 6e2852f4eea06750ea0f348ba25a5e3f24ec084c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 11:01:17 +0100 Subject: [PATCH 0878/1066] Fix #66685 --- .../parts/markers/electron-browser/markersPanelActions.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts index 278570fc877..43090745bbb 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanelActions.ts @@ -354,6 +354,9 @@ export class QuickFixActionItem extends ActionItem { if (!this.element) { return; } + if (!this.isEnabled()) { + return; + } const elementPosition = DOM.getDomNodePagePosition(this.element); this.markerWorkbenchService.getQuickFixActions((this.getAction()).marker).then(actions => { this.contextMenuService.showContextMenu({ From 4e57d8d4612a38da4b54dc783541239728343dee Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 11:07:17 +0100 Subject: [PATCH 0879/1066] fixes #67633 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 2485cf1bd9e..a4f7128419d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -215,11 +215,7 @@ function asObjectTreeOptions(options?: IAsyncDataTreeOpt return options.keyboardNavigationLabelProvider!.getKeyboardNavigationLabel(e.element as T); } }, - sorter: options.sorter && { - compare(a, b) { - return options.sorter!.compare(a.element as T, b.element as T); - } - } + sorter: undefined }; } @@ -263,6 +259,7 @@ export class AsyncDataTree implements IDisposable private readonly tree: ObjectTree, TFilterData>; private readonly root: IAsyncDataTreeNode; private readonly renderedNodes = new Map>(); + private readonly sorter?: ITreeSorter; private readonly subTreeRefreshPromises = new Map, Promise>(); private readonly refreshPromises = new Map, CancelablePromise>(); @@ -298,6 +295,7 @@ export class AsyncDataTree implements IDisposable ) { this.identityProvider = options.identityProvider; this.autoExpandSingleChildren = typeof options.autoExpandSingleChildren === 'undefined' ? false : options.autoExpandSingleChildren; + this.sorter = options.sorter; const objectTreeDelegate = new ComposedTreeDelegate>(delegate); const objectTreeRenderers = renderers.map(r => new DataTreeRenderer(r, this._onDidChangeNodeSlowState.event)); @@ -693,7 +691,15 @@ export class AsyncDataTree implements IDisposable return result; } - result = createCancelablePromise(_ => Promise.resolve(this.dataSource.getChildren(node.element!))); + result = createCancelablePromise(async () => { + const children = await this.dataSource.getChildren(node.element!); + + if (this.sorter) { + children.sort(this.sorter.compare.bind(this.sorter)); + } + + return children; + }); this.refreshPromises.set(node, result); return always(result, () => this.refreshPromises.delete(node)); } From 23e901b7529788f58214d9b5a3cc63c37419cf21 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 31 Jan 2019 11:13:28 +0100 Subject: [PATCH 0880/1066] fix #67626 --- .../parts/snippets/electron-browser/snippetsService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts index 524250d2cf2..7a288503970 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetsService.ts @@ -301,8 +301,11 @@ class SnippetsService implements ISnippetsService { private _initFolderSnippets(source: SnippetSource, folder: URI, bucket: IDisposable[]): Promise { let disposables: IDisposable[] = []; - let addFolderSnippets = () => { + let addFolderSnippets = (type?: FileChangeType) => { disposables = dispose(disposables); + if (type === FileChangeType.DELETED) { + return Promise.resolve(); + } return this._fileService.resolveFile(folder).then(stat => { for (const entry of stat.children || []) { disposables.push(this._addSnippetFile(entry.resource, source)); From 01aff5ab6c9d76177e8c42a80be7eff711c14355 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 11:33:35 +0100 Subject: [PATCH 0881/1066] explorer: Ignore first active editor change, since on startup we already reveal the active editor --- .../parts/files/electron-browser/views/explorerView.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 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 4f3b10f4513..0b558779712 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -63,7 +63,8 @@ export class ExplorerView extends ViewletPanel { private dragHandler: DelayedDragHandler; private decorationProvider: ExplorerDecorationsProvider; private autoReveal = false; - private ignoreActiveEditorChange; + // Ignore first active editor change, since on startup we already reveal the active editor + private ignoreActiveEditorChange = true; constructor( options: IViewletPanelOptions, @@ -196,6 +197,7 @@ export class ExplorerView extends ViewletPanel { if (!this.ignoreActiveEditorChange) { this.selectActiveFile(); } + this.ignoreActiveEditorChange = false; })); // Also handle configuration updates @@ -338,10 +340,7 @@ export class ExplorerView extends ViewletPanel { this.telemetryService.publicLog('workbenchActionExecuted', { id: 'workbench.files.openFile', from: 'explorer' }); this.ignoreActiveEditorChange = true; this.editorService.openEditor({ resource: selection[0].resource, options: { preserveFocus: (e.browserEvent instanceof MouseEvent) && !isDoubleClick, pinned: isDoubleClick || isMiddleClick } }, sideBySide ? SIDE_GROUP : ACTIVE_GROUP) - .then(() => this.ignoreActiveEditorChange = false).catch(e => { - this.ignoreActiveEditorChange = false; - onUnexpectedError(e); - }); + .then(undefined, onUnexpectedError); } })); From c4dc6df3fc313c45fd7603ace6415e4d9e9096fc Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 11:43:38 +0100 Subject: [PATCH 0882/1066] fixes #67570 --- src/vs/workbench/parts/files/electron-browser/fileActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index 0b22d39d37d..f38833fd7cd 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -484,7 +484,7 @@ class PasteFileAction extends BaseErrorReportingAction { } return undefined; - }); + }, e => this.onError(e)); }, error => { this.onError(new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile"))); }); From d3ea4a5aa77b06c6d2de82207315449dafb29244 Mon Sep 17 00:00:00 2001 From: Yan Pashkovsky Date: Thu, 31 Jan 2019 13:42:37 +0300 Subject: [PATCH 0883/1066] rejected promise not handled verbose message --- .../services/extensions/node/extensionHostProcess.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index 5f2935618c6..40f22930a7e 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -107,9 +107,14 @@ function connectToRenderer(protocol: IMessagePassingProtocol): Promise { const idx = unhandledPromises.indexOf(promise); if (idx >= 0) { - unhandledPromises.splice(idx, 1); - console.warn('rejected promise not handled within 1 second'); - onUnexpectedError(reason); + promise.catch(e => { + unhandledPromises.splice(idx, 1); + console.warn(`rejected promise not handled within 1 second: ${e}`); + if (e.stack) { + console.warn(`stack trace: ${e.stack}`); + } + onUnexpectedError(reason); + }); } }, 1000); }); From 51765d81b29e78e8619bb9d0ad0a86e4bf03bc74 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 31 Jan 2019 11:52:40 +0100 Subject: [PATCH 0884/1066] Fixes #67350: Do not allow to add an extension if it is already known at a different version --- .../extensions/electron-browser/extensionService.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 36eafb01844..b6b74109a3a 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -188,6 +188,12 @@ export class ExtensionService extends Disposable implements IExtensionService { continue; } + const existingExtensionDescription = this._registry.getExtensionDescription(extension.identifier.id); + if (existingExtensionDescription) { + // this extension is already running (most likely at a different version) + continue; + } + const extensionDescription = await this._extensionScanner.scanSingleExtension(extension.location.fsPath, extension.type === ExtensionType.System, this.createLogger()); if (!extensionDescription || !this._usesOnlyDynamicExtensionPoints(extensionDescription)) { // uses non-dynamic extension point From f6e47cf44855571152dd12f9d490530134d820d2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 13:40:45 +0100 Subject: [PATCH 0885/1066] hide complete container --- .../electron-browser/media/extensionsViewlet.css | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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 9089f6ff17d..020d8a4bcfa 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionsViewlet.css @@ -108,11 +108,8 @@ bottom: 5px; } -.extensions-viewlet.narrow > .extensions .extension > .icon-container > .icon { - display: none; -} - -.extensions-viewlet > .extensions .extension.loading > .icon-container > .icon { +.extensions-viewlet.narrow > .extensions .extension > .icon-container, +.extensions-viewlet > .extensions .extension.loading > .icon-container { display: none; } From e990eae5993a2ef84fdca6ebe26ecb77441bdb93 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 13:42:42 +0100 Subject: [PATCH 0886/1066] update title --- .../parts/extensions/electron-browser/extensionsWidgets.ts | 2 +- 1 file changed, 1 insertion(+), 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 6a49044725b..09f35375ec2 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsWidgets.ts @@ -240,7 +240,7 @@ export class RemoteBadgeWidget extends ExtensionWidget { this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables); this.workspaceContextService.onDidChangeWorkbenchState(applyBadgeStyle, this, this.disposables); - const updateTitle = () => this.element.title = this.labelService.getHostLabel(); + const updateTitle = () => this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel()); this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables); updateTitle(); } From bd903dde721cfe02c1d07f10e42b6e802d0863f1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 31 Jan 2019 14:01:09 +0100 Subject: [PATCH 0887/1066] fix #67390 --- .../editor/contrib/smartSelect/smartSelect.ts | 28 ++++++++++++++++++- .../smartSelect/test/smartSelect.test.ts | 15 ++++------ .../contrib/smartSelect/wordSelections.ts | 14 +++++----- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 19ed2246954..66d7733573b 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -241,6 +241,11 @@ export function provideSelectionRanges(model: ITextModel, position: Position, to } return Promise.all(work).then(() => { + + if (ranges.length === 0) { + return []; + } + ranges.sort((a, b) => { if (Position.isBefore(a.range.getStartPosition(), b.range.getStartPosition())) { return 1; @@ -254,6 +259,7 @@ export function provideSelectionRanges(model: ITextModel, position: Position, to return b.rank - a.rank; } }); + let result: Range[] = []; let last: Range | undefined; for (const { range } of ranges) { @@ -262,7 +268,27 @@ export function provideSelectionRanges(model: ITextModel, position: Position, to last = range; } } - return result; + + let result2: Range[] = [result[0]]; + for (let i = 1; i < result.length; i++) { + const prev = result[i - 1]; + const cur = result[i]; + if (cur.startLineNumber !== prev.startLineNumber || cur.endLineNumber !== prev.endLineNumber) { + // add line/block range without leading/failing whitespace + const rangeNoWhitespace = new Range(prev.startLineNumber, model.getLineFirstNonWhitespaceColumn(prev.startLineNumber), prev.endLineNumber, model.getLineLastNonWhitespaceColumn(prev.endLineNumber)); + if (rangeNoWhitespace.containsRange(prev) && !rangeNoWhitespace.equalsRange(prev)) { + result2.push(rangeNoWhitespace); + } + // add line/block range + const rangeFull = new Range(prev.startLineNumber, 1, prev.endLineNumber, model.getLineMaxColumn(prev.endLineNumber)); + if (rangeFull.containsRange(prev) && !rangeFull.equalsRange(rangeNoWhitespace)) { + result2.push(rangeFull); + } + } + result2.push(cur); + } + + return result2; }); } diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index efe1e8d2056..e8a5ec3cbdd 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -83,7 +83,7 @@ suite('SmartSelect', () => { let actualStr = actual!.map(r => new Range(r.startLineNumber, r.startColumn, r.endLineNumber, r.endColumn).toString()); let desiredStr = ranges.reverse().map(r => String(r)); - assert.deepEqual(actualStr, desiredStr); + assert.deepEqual(actualStr, desiredStr, `\nA: ${actualStr} VS \nE: ${desiredStr}`); modelService.destroyModel(uri); } @@ -99,6 +99,7 @@ suite('SmartSelect', () => { new Range(1, 1, 5, 2), // all new Range(1, 21, 5, 2), // {} outside new Range(1, 22, 5, 1), // {} inside + new Range(2, 1, 4, 3), // block new Range(2, 1, 4, 3), new Range(2, 2, 4, 3), new Range(2, 11, 4, 3), @@ -125,10 +126,10 @@ suite('SmartSelect', () => { new Range(1, 21, 5, 2), new Range(1, 22, 5, 1), new Range(2, 1, 4, 3), + new Range(2, 1, 4, 3), new Range(2, 2, 4, 3), new Range(2, 11, 4, 3), new Range(2, 12, 4, 2), - new Range(3, 1, 3, 1), ]); }); @@ -145,10 +146,12 @@ suite('SmartSelect', () => { new Range(1, 21, 5, 2), // {} outside new Range(1, 22, 5, 1), // {} inside new Range(2, 1, 4, 3), + new Range(2, 1, 4, 3), new Range(2, 2, 4, 3), new Range(2, 11, 4, 3), new Range(2, 12, 4, 2), - new Range(3, 1, 3, 2) // line w/ triva + new Range(3, 1, 3, 2), // block + new Range(3, 1, 3, 2) // empty line ]); }); @@ -261,8 +264,6 @@ suite('SmartSelect', () => { await assertRanges(new WordSelectionRangeProvider(), 'fIooBar', new Range(1, 1, 1, 5), // foo new Range(1, 1, 1, 8), // fooBar - new Range(1, 1, 1, 8), // line (triva) - new Range(1, 1, 1, 8), // line new Range(1, 1, 1, 8), // doc ); @@ -270,16 +271,12 @@ suite('SmartSelect', () => { new Range(1, 1, 1, 5), new Range(1, 1, 1, 8), new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), ); await assertRanges(new WordSelectionRangeProvider(), 'fIoo-Ba', new Range(1, 1, 1, 5), new Range(1, 1, 1, 8), new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), - new Range(1, 1, 1, 8), ); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index 512e0352654..0d5d5fa2ca7 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -16,7 +16,7 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { let result: SelectionRange[] = []; this._addInWordRanges(result, model, position); this._addWordRanges(result, model, position); - this._addLineRanges(result, model, position); + this._addWhitespaceLine(result, model, position); result.push({ range: model.getFullModelRange(), kind: 'statement.all' }); return result; } @@ -60,12 +60,12 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { } } - private _addLineRanges(bucket: SelectionRange[], model: ITextModel, pos: Position): void { - - const nonWhitespaceColumn = model.getLineFirstNonWhitespaceColumn(pos.lineNumber); - if (nonWhitespaceColumn > 0) { - bucket.push({ range: new Range(pos.lineNumber, nonWhitespaceColumn, pos.lineNumber, model.getLineLastNonWhitespaceColumn(pos.lineNumber)), kind: 'statement.line' }); + private _addWhitespaceLine(bucket: SelectionRange[], model: ITextModel, pos: Position): void { + if (model.getLineLength(pos.lineNumber) > 0 + && model.getLineFirstNonWhitespaceColumn(pos.lineNumber) === 0 + && model.getLineLastNonWhitespaceColumn(pos.lineNumber) === 0 + ) { + bucket.push({ range: new Range(pos.lineNumber, 1, pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)), kind: 'statement.line' }); } - bucket.push({ range: new Range(pos.lineNumber, model.getLineMinColumn(pos.lineNumber), pos.lineNumber, model.getLineMaxColumn(pos.lineNumber)), kind: 'statement.line.full' }); } } From d220c955d4c00cd13c0a0a952f02d2cba2e253e8 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 14:02:01 +0100 Subject: [PATCH 0888/1066] #67585 remove border radius --- .../extensions/electron-browser/media/extensionActions.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css index fbfdba03550..d2bea0aecb4 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css +++ b/src/vs/workbench/parts/extensions/electron-browser/media/extensionActions.css @@ -49,12 +49,12 @@ .monaco-action-bar .action-item.disabled .action-label.extension-status-label { opacity: 0.9; line-height: initial; - border-radius: 4px; padding: 0 5px; } .monaco-action-bar .action-item .action-label.disable-status, .monaco-action-bar .action-item .action-label.malicious-status { + border-radius: 4px; color: inherit; background-color: transparent; font-style: italic; From 70160d0e580d0588e37c93fe8724014d76b00939 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 31 Jan 2019 14:51:25 +0100 Subject: [PATCH 0889/1066] fix #67493 --- .../smartSelect/test/smartSelect.test.ts | 71 ++++++++++++++----- .../contrib/smartSelect/wordSelections.ts | 48 ++++++++----- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts index e8a5ec3cbdd..43b94bd2d05 100644 --- a/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/smartSelect.test.ts @@ -55,7 +55,7 @@ class MockJSMode extends MockMode { ], onEnterRules: javascriptOnEnterRules, - wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\=\+\[\{\]\}\\\;\:\'\"\,\.\<\>\/\?\s]+)/g })); } } @@ -202,7 +202,7 @@ suite('SmartSelect', () => { async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise { let model = modelService.createModel(value, new StaticLanguageSelector(mode.getLanguageIdentifier()), URI.parse('fake:lang')); - let pos = model.getPositionAt(value.indexOf('I')); + let pos = model.getPositionAt(value.indexOf('|')); let ranges = await provider.provideSelectionRanges(model, pos, CancellationToken.None); modelService.destroyModel(model.uri); @@ -214,45 +214,45 @@ suite('SmartSelect', () => { } test('bracket selection', async () => { - await assertRanges(new BracketSelectionRangeProvider(), '(I)', + await assertRanges(new BracketSelectionRangeProvider(), '(|)', new Range(1, 2, 1, 3), new Range(1, 1, 1, 4) ); - await assertRanges(new BracketSelectionRangeProvider(), '[[[](I)]]', + await assertRanges(new BracketSelectionRangeProvider(), '[[[](|)]]', new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), // () new Range(1, 3, 1, 8), new Range(1, 2, 1, 9), // [[]()] new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), // [[[]()]] ); - await assertRanges(new BracketSelectionRangeProvider(), '[a[](I)a]', + await assertRanges(new BracketSelectionRangeProvider(), '[a[](|)a]', new Range(1, 6, 1, 7), new Range(1, 5, 1, 8), new Range(1, 2, 1, 9), new Range(1, 1, 1, 10), ); // no bracket - await assertRanges(new BracketSelectionRangeProvider(), 'fofofIfofo'); + await assertRanges(new BracketSelectionRangeProvider(), 'fofof|fofo'); // empty - await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]]I'); - await assertRanges(new BracketSelectionRangeProvider(), 'I[[[]()]]'); + await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]]|'); + await assertRanges(new BracketSelectionRangeProvider(), '|[[[]()]]'); // edge - await assertRanges(new BracketSelectionRangeProvider(), '[I[[]()]]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); - await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]I]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); + await assertRanges(new BracketSelectionRangeProvider(), '[|[[]()]]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); + await assertRanges(new BracketSelectionRangeProvider(), '[[[]()]|]', new Range(1, 2, 1, 9), new Range(1, 1, 1, 10)); - await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(bIb)ccc(ccc)', new Range(1, 13, 1, 16), new Range(1, 12, 1, 17)); - await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(bIb)ccc(ccc))', new Range(1, 14, 1, 17), new Range(1, 13, 1, 18), new Range(1, 2, 1, 26), new Range(1, 1, 1, 27)); + await assertRanges(new BracketSelectionRangeProvider(), 'aaa(aaa)bbb(b|b)ccc(ccc)', new Range(1, 13, 1, 16), new Range(1, 12, 1, 17)); + await assertRanges(new BracketSelectionRangeProvider(), '(aaa(aaa)bbb(b|b)ccc(ccc))', new Range(1, 14, 1, 17), new Range(1, 13, 1, 18), new Range(1, 2, 1, 26), new Range(1, 1, 1, 27)); }); test('bracket with leading/trailing', async () => { - await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(I);\n}', + await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b){\n foo(|);\n}', new Range(2, 7, 2, 8), new Range(2, 6, 2, 9), new Range(1, 13, 3, 1), new Range(1, 12, 3, 2), new Range(1, 1, 3, 2), new Range(1, 1, 3, 2), ); - await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(I);\n}', + await assertRanges(new BracketSelectionRangeProvider(), 'for(a of b)\n{\n foo(|);\n}', new Range(3, 7, 3, 8), new Range(3, 6, 3, 9), new Range(2, 2, 4, 1), new Range(2, 1, 4, 2), new Range(1, 1, 4, 2), new Range(1, 1, 4, 2), @@ -261,26 +261,61 @@ suite('SmartSelect', () => { test('in-word ranges', async () => { - await assertRanges(new WordSelectionRangeProvider(), 'fIooBar', + await assertRanges(new WordSelectionRangeProvider(), 'f|ooBar', new Range(1, 1, 1, 5), // foo new Range(1, 1, 1, 8), // fooBar new Range(1, 1, 1, 8), // doc ); - await assertRanges(new WordSelectionRangeProvider(), 'fIoo_Ba', + await assertRanges(new WordSelectionRangeProvider(), 'f|oo_Ba', new Range(1, 1, 1, 5), new Range(1, 1, 1, 8), new Range(1, 1, 1, 8), ); - await assertRanges(new WordSelectionRangeProvider(), 'fIoo-Ba', + await assertRanges(new WordSelectionRangeProvider(), 'f|oo-Ba', new Range(1, 1, 1, 5), new Range(1, 1, 1, 8), new Range(1, 1, 1, 8), ); }); - test('Default selection should select current word/hump first in camelCase #67493', function () { + test('Default selection should select current word/hump first in camelCase #67493', async function () { + await assertRanges(new WordSelectionRangeProvider(), 'Abs|tractSmartSelect', + new Range(1, 1, 1, 10), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'AbstractSma|rtSelect', + new Range(1, 9, 1, 15), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Abstrac-Sma|rt-elect', + new Range(1, 9, 1, 15), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt_elect', + new Range(1, 9, 1, 15), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rt-elect', + new Range(1, 9, 1, 15), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); + + await assertRanges(new WordSelectionRangeProvider(), 'Abstrac_Sma|rtSelect', + new Range(1, 9, 1, 15), + new Range(1, 1, 1, 21), + new Range(1, 1, 1, 21), + ); }); }); diff --git a/src/vs/editor/contrib/smartSelect/wordSelections.ts b/src/vs/editor/contrib/smartSelect/wordSelections.ts index 0d5d5fa2ca7..6c6c1ffd65b 100644 --- a/src/vs/editor/contrib/smartSelect/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/wordSelections.ts @@ -26,31 +26,43 @@ export class WordSelectionRangeProvider implements SelectionRangeProvider { if (!obj) { return; } + let { word, startColumn } = obj; let offset = pos.column - startColumn; + let start = offset; + let end = offset; let lastCh: number = 0; - for (; offset < word.length; offset++) { - let ch = word.charCodeAt(offset); - if (isUpperAsciiLetter(ch) && isLowerAsciiLetter(lastCh)) { + + // LEFT anchor (start) + for (; start >= 0; start--) { + let ch = word.charCodeAt(start); + if (ch === CharCode.Underline || ch === CharCode.Dash) { + // foo-bar OR foo_bar + break; + } else if (isLowerAsciiLetter(ch) && isUpperAsciiLetter(lastCh)) { // fooBar - // ^^^ - // ^^^^^^ - bucket.push({ range: new Range(pos.lineNumber, startColumn, pos.lineNumber, startColumn + offset), kind: 'statement.word.part' }); - } else if (ch === CharCode.Underline && lastCh !== CharCode.Underline) { - // foo_bar - // ^^^ - // ^^^^^^^ - bucket.push({ range: new Range(pos.lineNumber, startColumn, pos.lineNumber, startColumn + offset), kind: 'statement.word.part' }); - offset += 1; - } else if (ch === CharCode.Dash && lastCh !== CharCode.Dash) { - // foo-bar - // ^^^ - // ^^^^^^^ - bucket.push({ range: new Range(pos.lineNumber, startColumn, pos.lineNumber, startColumn + offset), kind: 'statement.word.part' }); - offset += 1; + break; } lastCh = ch; } + start += 1; + + // RIGHT anchor (end) + for (; end < word.length; end++) { + let ch = word.charCodeAt(end); + if (isUpperAsciiLetter(ch) && isLowerAsciiLetter(lastCh)) { + // fooBar + break; + } else if (ch === CharCode.Underline || ch === CharCode.Dash) { + // foo-bar OR foo_bar + break; + } + lastCh = ch; + } + + if (start < end) { + bucket.push({ range: new Range(pos.lineNumber, startColumn + start, pos.lineNumber, startColumn + end), kind: 'statement.word.part' }); + } } private _addWordRanges(bucket: SelectionRange[], model: ITextModel, pos: Position): void { From 5f842e20926bbaddc3338bb06294763d4a8e3cbf Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 15:19:18 +0100 Subject: [PATCH 0890/1066] fixes #66995 --- src/vs/base/browser/ui/tree/abstractTree.ts | 78 +++++++++++++------ src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 + src/vs/platform/list/browser/listService.ts | 47 +++++++++++ .../workbench/browser/actions/listCommands.ts | 23 +++++- 4 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 56ea99c13f0..67f91e2f92d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -377,6 +377,9 @@ class TypeFilterController implements IDisposable { private filterOnTypeDomNode: HTMLInputElement; private clearDomNode: HTMLElement; + private automaticKeyboardNavigation: boolean; + private triggered = false; + private enabledDisposables: IDisposable[] = []; private disposables: IDisposable[] = []; @@ -408,12 +411,10 @@ class TypeFilterController implements IDisposable { this.clearDomNode.title = localize('clear', "Clear"); model.onDidSplice(this.onDidSpliceModel, this, this.disposables); - - tree.onDidUpdateOptions(this.onDidUpdateTreeOptions, this, this.disposables); - this.onDidUpdateTreeOptions(tree.options); + this.updateOptions(tree.options); } - private onDidUpdateTreeOptions(options: IAbstractTreeOptions): void { + updateOptions(options: IAbstractTreeOptions): void { if (options.simpleKeyboardNavigation) { this.disable(); } else { @@ -421,10 +422,19 @@ class TypeFilterController implements IDisposable { } this.filterOnTypeDomNode.checked = !!options.filterOnType; + this.automaticKeyboardNavigation = typeof options.automaticKeyboardNavigation === 'undefined' ? true : options.automaticKeyboardNavigation; this.tree.refilter(); this.render(); } + toggle(): void { + this.triggered = !this.triggered; + + if (!this.triggered) { + this.onEventOrInput(''); + } + } + private enable(): void { if (this._enabled) { return; @@ -433,31 +443,22 @@ class TypeFilterController implements IDisposable { const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) + .filter(() => this.automaticKeyboardNavigation || this.triggered) .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) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey)))) + .filter(e => isPrintableCharEvent(e) || ((this._pattern.length > 0 || this.triggered) && ((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 || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { - return ''; - } - if (e.keyCode === KeyCode.Backspace) { - return previous.length === 0 ? '' : previous.substr(0, previous.length - 1); - } + Event.chain(Event.any(onKeyDown, onClear)) + .event(this.onEventOrInput, this, this.enabledDisposables); - return previous + e.browserEvent.key; - }, '') - .event; - - onInput(this.onInput, this, this.enabledDisposables); this.filter.pattern = ''; this.tree.refilter(); this.render(); this._enabled = true; + this.triggered = false; } private disable(): void { @@ -470,6 +471,19 @@ class TypeFilterController implements IDisposable { this.tree.refilter(); this.render(); this._enabled = false; + this.triggered = false; + } + + private onEventOrInput(e: MouseEvent | StandardKeyboardEvent | string): void { + if (typeof e === 'string') { + this.onInput(e); + } else if (e instanceof MouseEvent || e.keyCode === KeyCode.Escape || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey))) { + this.onInput(''); + } else if (e.keyCode === KeyCode.Backspace) { + this.onInput(this.pattern.length === 0 ? '' : this.pattern.substr(0, this.pattern.length - 1)); + } else { + this.onInput(this.pattern + e.browserEvent.key); + } } private onInput(pattern: string): void { @@ -498,6 +512,10 @@ class TypeFilterController implements IDisposable { } this.render(); + + if (!pattern) { + this.triggered = false; + } } private onDragStart(): void { @@ -631,6 +649,7 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent implements IDisposable private focus = new Trait(); private selection = new Trait(); private eventBufferer = new EventBufferer(); + private typeFilterController?: TypeFilterController; protected disposables: IDisposable[] = []; - private _onDidUpdateOptions = new Emitter>(); - readonly onDidUpdateOptions = this._onDidUpdateOptions.event; - get onDidScroll(): Event { return this.view.onDidScroll; } readonly onDidChangeFocus: Event> = this.eventBufferer.wrapEvent(this.focus.onDidChange); @@ -852,9 +869,9 @@ export abstract class AbstractTree implements IDisposable } if (_options.keyboardNavigationLabelProvider) { - const typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); + this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); this.focusNavigationFilter = node => { - if (!typeFilterController.enabled || !typeFilterController.pattern) { + if (!this.typeFilterController!.enabled || !this.typeFilterController!.pattern) { return true; } @@ -864,7 +881,7 @@ export abstract class AbstractTree implements IDisposable return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); }; - this.disposables.push(typeFilterController); + this.disposables.push(this.typeFilterController!); } } @@ -876,7 +893,10 @@ export abstract class AbstractTree implements IDisposable } this.view.updateOptions({ enableKeyboardNavigation: this._options.simpleKeyboardNavigation }); - this._onDidUpdateOptions.fire(this._options); + + if (this.typeFilterController) { + this.typeFilterController.updateOptions(this._options); + } } get options(): IAbstractTreeOptions { @@ -983,6 +1003,14 @@ export abstract class AbstractTree implements IDisposable return this.model.isCollapsed(location); } + toggleKeyboardNavigation(): void { + if (!this.typeFilterController) { + return; + } + + this.typeFilterController.toggle(); + } + refilter(): void { this._onWillRefilter.fire(undefined); this.model.refilter(); diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index a4f7128419d..3b50d3ff5f8 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -478,6 +478,10 @@ export class AsyncDataTree implements IDisposable return this.tree.isCollapsed(this.getDataNode(element)); } + toggleKeyboardNavigation(): void { + this.tree.toggleKeyboardNavigation(); + } + refilter(): void { this.tree.refilter(); } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 776395ccbcf..529f63834ed 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -103,6 +103,8 @@ export const WorkbenchListFocusContextKey = ContextKeyExpr.and(RawWorkbenchListF export const WorkbenchListHasSelectionOrFocus = new RawContextKey('listHasSelectionOrFocus', false); export const WorkbenchListDoubleSelection = new RawContextKey('listDoubleSelection', false); export const WorkbenchListMultiSelection = new RawContextKey('listMultiSelection', false); +export const WorkbenchListAutomaticKeyboardNavigationKey = 'listAutomaticKeyboardNavigation'; +export const WorkbenchListAutomaticKeyboardNavigation = new RawContextKey(WorkbenchListAutomaticKeyboardNavigationKey, true); function createScopedContextKeyService(contextKeyService: IContextKeyService, widget: ListWidget): IContextKeyService { const result = contextKeyService.createScoped(widget.getHTMLElement()); @@ -920,6 +922,9 @@ export class WorkbenchObjectTree, TFilterData = void> @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); + + const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); @@ -929,6 +934,7 @@ export class WorkbenchObjectTree, TFilterData = void> ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService), indent: configurationService.getValue(treeIndentKey), + automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', horizontalScrolling @@ -946,6 +952,9 @@ export class WorkbenchObjectTree, TFilterData = void> this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); + const interestingContextKeys = new Set(); + interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey); + this.disposables.push( this.contextKeyService, (listService as ListService).register(this), @@ -982,6 +991,14 @@ export class WorkbenchObjectTree, TFilterData = void> filterOnType: keyboardNavigation === 'filter' }); } + }), + this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(interestingContextKeys)) { + const automaticKeyboardNavigation = this.contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); + this.updateOptions({ + automaticKeyboardNavigation + }); + } }) ); } @@ -1023,6 +1040,9 @@ export class WorkbenchDataTree extends DataTree(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); @@ -1032,6 +1052,7 @@ export class WorkbenchDataTree extends DataTree extends DataTree extends DataTree { + if (e.affectsSome(interestingContextKeys)) { + const automaticKeyboardNavigation = this.contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); + this.updateOptions({ + automaticKeyboardNavigation + }); + } }) ); } @@ -1121,6 +1153,9 @@ export class WorkbenchAsyncDataTree extends Async @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); + + const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); @@ -1130,6 +1165,7 @@ export class WorkbenchAsyncDataTree extends Async ...computeStyles(themeService.getTheme(), defaultListStyles), ...toWorkbenchListOptions(options, configurationService, keybindingService), indent: configurationService.getValue(treeIndentKey), + automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', horizontalScrolling @@ -1147,6 +1183,9 @@ export class WorkbenchAsyncDataTree extends Async this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); + const interestingContextKeys = new Set(); + interestingContextKeys.add(WorkbenchListAutomaticKeyboardNavigationKey); + this.disposables.push( this.contextKeyService, (listService as ListService).register(this), @@ -1183,6 +1222,14 @@ export class WorkbenchAsyncDataTree extends Async filterOnType: keyboardNavigation === 'filter' }); } + }), + this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(interestingContextKeys)) { + const automaticKeyboardNavigation = this.contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); + this.updateOptions({ + automaticKeyboardNavigation + }); + } }) ); } diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 5fbd81fae8e..123778a5809 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -7,7 +7,7 @@ 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 { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget, WorkbenchListHasSelectionOrFocus, WorkbenchListAutomaticKeyboardNavigationKey } 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'; @@ -16,6 +16,7 @@ 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'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; function ensureDOMFocus(widget: ListWidget): void { // it can happen that one of the commands is executed while @@ -742,4 +743,22 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ } } } -}); \ No newline at end of file +}); + +CommandsRegistry.registerCommand({ + id: 'list.toggleKeyboardNavigation', + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList) { + // TODO@joao + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + tree.toggleKeyboardNavigation(); + } + } +}); From c9695877372a07dd1bfd137c9cf3e02ba2847031 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 31 Jan 2019 15:24:29 +0100 Subject: [PATCH 0891/1066] fix build --- src/vs/workbench/browser/actions/listCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 123778a5809..087f1f5f2aa 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -7,7 +7,7 @@ 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, WorkbenchListAutomaticKeyboardNavigationKey } from 'vs/platform/list/browser/listService'; +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'; From effdac4b6b9512ab924c37577535583fae12a7c4 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 31 Jan 2019 15:31:57 +0100 Subject: [PATCH 0892/1066] Fix #67585 --- .../parts/extensions/electron-browser/extensionsActions.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index de2c77c9b6a..33ff76a0343 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -2610,17 +2610,13 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const extensionButtonProminentBackgroundColor = theme.getColor(extensionButtonProminentBackground); if (extensionButtonProminentBackground) { collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-status-label { background-color: ${extensionButtonProminentBackgroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { background-color: ${extensionButtonProminentBackgroundColor}; }`); - collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-status-label { background-color: ${extensionButtonProminentBackgroundColor}; }`); } const extensionButtonProminentForegroundColor = theme.getColor(extensionButtonProminentForeground); if (extensionButtonProminentForeground) { collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); - collector.addRule(`.extension .monaco-action-bar .action-item .action-label.extension-status-label { color: ${extensionButtonProminentForegroundColor}; }`); collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-action.prominent { color: ${extensionButtonProminentForegroundColor}; }`); - collector.addRule(`.extension-editor .monaco-action-bar .action-item .action-label.extension-status-label { color: ${extensionButtonProminentForegroundColor}; }`); } const extensionButtonProminentHoverBackgroundColor = theme.getColor(extensionButtonProminentHoverBackground); From 714824761b36ec8d4526739947af0fa25df83831 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 15:34:59 +0100 Subject: [PATCH 0893/1066] add list keyboard navigation support context key related to #66995 --- src/vs/base/browser/ui/tree/abstractTree.ts | 4 ++++ src/vs/platform/list/browser/listService.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 67f91e2f92d..95b8617c300 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -425,6 +425,10 @@ class TypeFilterController implements IDisposable { this.automaticKeyboardNavigation = typeof options.automaticKeyboardNavigation === 'undefined' ? true : options.automaticKeyboardNavigation; this.tree.refilter(); this.render(); + + if (!this.automaticKeyboardNavigation) { + this.onEventOrInput(''); + } } toggle(): void { diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 529f63834ed..84f5114a9a9 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -103,6 +103,7 @@ export const WorkbenchListFocusContextKey = ContextKeyExpr.and(RawWorkbenchListF export const WorkbenchListHasSelectionOrFocus = new RawContextKey('listHasSelectionOrFocus', false); export const WorkbenchListDoubleSelection = new RawContextKey('listDoubleSelection', false); export const WorkbenchListMultiSelection = new RawContextKey('listMultiSelection', false); +export const WorkbenchListSupportsKeyboardNavigation = new RawContextKey('listSupportsKeyboardNavigation', true); export const WorkbenchListAutomaticKeyboardNavigationKey = 'listAutomaticKeyboardNavigation'; export const WorkbenchListAutomaticKeyboardNavigation = new RawContextKey(WorkbenchListAutomaticKeyboardNavigationKey, true); @@ -922,6 +923,7 @@ export class WorkbenchObjectTree, TFilterData = void> @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService); WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); @@ -1040,6 +1042,7 @@ export class WorkbenchDataTree extends DataTree(WorkbenchListAutomaticKeyboardNavigationKey); @@ -1153,6 +1156,7 @@ export class WorkbenchAsyncDataTree extends Async @IConfigurationService configurationService: IConfigurationService, @IKeybindingService keybindingService: IKeybindingService ) { + WorkbenchListSupportsKeyboardNavigation.bindTo(contextKeyService); WorkbenchListAutomaticKeyboardNavigation.bindTo(contextKeyService); const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); From f41ddf2bb65d552d7e7e54f66101afe2edbfb963 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 15:42:43 +0100 Subject: [PATCH 0894/1066] fixes #67449 --- 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 95b8617c300..b57109987e0 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -879,7 +879,7 @@ export abstract class AbstractTree implements IDisposable return true; } - if (filter!.totalCount > 0 && filter!.matchCount === 0) { + if (filter!.totalCount > 0 && filter!.matchCount <= 1) { return true; } From 5aaf8173b33ecb2df7f2b836fa85813ec6df5b75 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 16:02:32 +0100 Subject: [PATCH 0895/1066] add filter matches to references widget related to #67280 --- .../contrib/referenceSearch/referencesTree.ts | 17 +++++++++-------- .../contrib/referenceSearch/referencesWidget.ts | 7 ++++--- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/referenceSearch/referencesTree.ts b/src/vs/editor/contrib/referenceSearch/referencesTree.ts index 296d55db965..8866c65be81 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesTree.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesTree.ts @@ -24,6 +24,7 @@ import { IListVirtualDelegate, IKeyboardNavigationLabelProvider, IIdentityProvid import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { basename } from 'vs/base/common/paths'; +import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; //#region data source @@ -115,7 +116,7 @@ class FileReferencesTemplate extends Disposable { super(); const parent = document.createElement('div'); dom.addClass(parent, 'reference-file'); - this.file = this._register(new IconLabel(parent)); + this.file = this._register(new IconLabel(parent, { supportHighlights: true })); this.badge = new CountBadge(dom.append(parent, dom.$('.count'))); this._register(attachBadgeStyler(this.badge, themeService)); @@ -123,9 +124,9 @@ class FileReferencesTemplate extends Disposable { container.appendChild(parent); } - set(element: FileReferences) { + set(element: FileReferences, matches: IMatch[]) { let parent = dirname(element.uri); - this.file.setLabel(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri) }); + this.file.setLabel(getBaseLabel(element.uri), parent ? this._uriLabel.getUriLabel(parent, { relative: true }) : undefined, { title: this._uriLabel.getUriLabel(element.uri), matches }); const len = element.children.length; this.badge.setCount(len); if (element.failure) { @@ -138,7 +139,7 @@ class FileReferencesTemplate extends Disposable { } } -export class FileReferencesRenderer implements ITreeRenderer { +export class FileReferencesRenderer implements ITreeRenderer { static readonly id = 'FileReferencesRenderer'; @@ -149,8 +150,8 @@ export class FileReferencesRenderer implements ITreeRenderer, index: number, template: FileReferencesTemplate): void { - template.set(node.element); + renderElement(node: ITreeNode, index: number, template: FileReferencesTemplate): void { + template.set(node.element, createMatches(node.filterData)); } disposeTemplate(templateData: FileReferencesTemplate): void { templateData.dispose(); @@ -191,7 +192,7 @@ class OneReferenceTemplate { } } -export class OneReferenceRenderer implements ITreeRenderer { +export class OneReferenceRenderer implements ITreeRenderer { static readonly id = 'OneReferenceRenderer'; @@ -200,7 +201,7 @@ export class OneReferenceRenderer implements ITreeRenderer, index: number, templateData: OneReferenceTemplate): void { + renderElement(element: ITreeNode, index: number, templateData: OneReferenceTemplate): void { templateData.set(element.element); } disposeTemplate(): void { diff --git a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts index e04ca9e6c5d..60500684060 100644 --- a/src/vs/editor/contrib/referenceSearch/referencesWidget.ts +++ b/src/vs/editor/contrib/referenceSearch/referencesWidget.ts @@ -34,6 +34,7 @@ import { FileReferences, OneReference, ReferencesModel } from './referencesModel import { ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; import { IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { FuzzyScore } from 'vs/base/common/filters'; class DecorationsManager implements IDisposable { @@ -245,7 +246,7 @@ export class ReferenceWidget extends PeekViewWidget { private _callOnDispose: IDisposable[] = []; private _onDidSelectReference = new Emitter(); - private _tree: WorkbenchAsyncDataTree; + private _tree: WorkbenchAsyncDataTree; private _treeContainer: HTMLElement; private _sash: VSash; private _preview: ICodeEditor; @@ -360,7 +361,7 @@ export class ReferenceWidget extends PeekViewWidget { this._instantiationService.createInstance(OneReferenceRenderer), ]; - const treeOptions: IAsyncDataTreeOptions = { + const treeOptions: IAsyncDataTreeOptions = { ariaLabel: nls.localize('treeAriaLabel', "References"), keyboardSupport: this._defaultTreeKeyboardSupport, accessibilityProvider: new AriaProvider(), @@ -370,7 +371,7 @@ export class ReferenceWidget extends PeekViewWidget { const treeDataSource = this._instantiationService.createInstance(DataSource); - this._tree = this._instantiationService.createInstance, ITreeRenderer[], IAsyncDataSource, IAsyncDataTreeOptions, WorkbenchAsyncDataTree>( + this._tree = this._instantiationService.createInstance, ITreeRenderer[], IAsyncDataSource, IAsyncDataTreeOptions, WorkbenchAsyncDataTree>( WorkbenchAsyncDataTree, this._treeContainer, new Delegate(), From ea5048a7d5c4dcb749949317648afe34664edf94 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 16:13:37 +0100 Subject: [PATCH 0896/1066] undefined keyboard navigation label always matches fixes #66979 --- src/vs/base/browser/ui/list/list.ts | 8 +++++++- src/vs/base/browser/ui/list/listWidget.ts | 3 ++- src/vs/base/browser/ui/tree/abstractTree.ts | 10 ++++++++-- src/vs/workbench/parts/files/common/files.ts | 1 + .../parts/files/electron-browser/explorerService.ts | 4 ++++ .../parts/files/electron-browser/views/explorerView.ts | 8 +++++++- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.ts b/src/vs/base/browser/ui/list/list.ts index 49c953f0124..b4a4aa4821f 100644 --- a/src/vs/base/browser/ui/list/list.ts +++ b/src/vs/base/browser/ui/list/list.ts @@ -71,7 +71,13 @@ export enum ListAriaRootRole { } export interface IKeyboardNavigationLabelProvider { - getKeyboardNavigationLabel(element: T): { toString(): string; }; + + /** + * Return a keyboard navigation label which will be used by the + * list for filtering/navigating. Return `undefined` to make an + * element always match. + */ + getKeyboardNavigationLabel(element: T): { toString(): string | undefined; } | undefined; 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 2cc18d00b3a..ebe2af766dc 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -403,8 +403,9 @@ class TypeLabelController implements IDisposable { for (let i = 0; i < this.list.length; i++) { const index = (start + i + delta) % this.list.length; const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(this.view.element(index)); + const labelStr = label && label.toString(); - if (matchesPrefix(word, label.toString())) { + if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { this.list.setFocus([index]); this.list.reveal(index); return; diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index b57109987e0..d7362e7e14d 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -334,8 +334,14 @@ class TypeFilter implements ITreeFilter, IDisposable { return { data: FuzzyScore.Default, visibility: true }; } - const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element).toString(); - const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, label, label.toLowerCase(), 0, true); + const label = this.keyboardNavigationLabelProvider.getKeyboardNavigationLabel(element); + const labelStr = label && label.toString(); + + if (typeof labelStr === 'undefined') { + return { data: FuzzyScore.Default, visibility: true }; + } + + const score = fuzzyScore(this._pattern, this._lowercasePattern, 0, labelStr, labelStr.toLowerCase(), 0, true); if (!score) { if (this.tree.options.filterOnType) { diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 10877a56430..8915bd13b1d 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -49,6 +49,7 @@ export interface IExplorerService { setEditable(stat: ExplorerItem, data: IEditableData): void; getEditableData(stat: ExplorerItem): IEditableData | undefined; + isEditable(stat: ExplorerItem): boolean; findClosest(resource: URI): ExplorerItem | null; refresh(): void; setToCopy(stats: ExplorerItem[], cut: boolean): void; diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index 234117fd39c..e6edda05b16 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -129,6 +129,10 @@ export class ExplorerService implements IExplorerService { return this.editableStats.get(stat); } + isEditable(stat: ExplorerItem): boolean { + return this.editableStats.has(stat); + } + select(resource: URI, reveal?: boolean): Promise { const fileStat = this.findClosest(resource); if (fileStat) { 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 0b558779712..a972a289ee2 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -266,7 +266,13 @@ export class ExplorerView extends ViewletPanel { getId: stat => stat.resource }, keyboardNavigationLabelProvider: { - getKeyboardNavigationLabel: stat => stat.name + getKeyboardNavigationLabel: stat => { + if (this.explorerService.isEditable(stat)) { + return undefined; + } + + return stat.name; + } }, multipleSelectionSupport: true, filter: this.filter, From 7cb0746b71bbd264b0bce6400903bd5ba0532e35 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 16:01:48 +0100 Subject: [PATCH 0897/1066] highlight theme rule for all new trees --- src/vs/workbench/browser/parts/views/customView.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index d0c1951d8bd..a6568b49c4e 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -615,12 +615,12 @@ registerThemingParticipant((theme, collector) => { const findMatchHighlightColor = theme.getColor(editorFindMatchHighlight); if (findMatchHighlightColor) { collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); - collector.addRule(`.file-icon-themable-tree .monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; background-color: ${findMatchHighlightColor}; }`); } const findMatchHighlightColorBorder = theme.getColor(editorFindMatchHighlightBorder); if (findMatchHighlightColorBorder) { collector.addRule(`.file-icon-themable-tree .monaco-tree-row .content .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); - collector.addRule(`.file-icon-themable-tree .monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); + collector.addRule(`.monaco-tl-contents .monaco-highlighted-label .highlight { color: unset !important; border: 1px dotted ${findMatchHighlightColorBorder}; box-sizing: border-box; }`); } const link = theme.getColor(textLinkForeground); if (link) { @@ -925,4 +925,4 @@ class MarkdownRenderer { dispose: () => dispose(disposeables) }; } -} \ No newline at end of file +} From ccdaf1272f61add807588570425b7dd94986a559 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 16:17:44 +0100 Subject: [PATCH 0898/1066] debug: dighlights in trees --- .../parts/debug/browser/baseDebugView.ts | 28 +++++++----- .../parts/debug/browser/debugActions.ts | 2 +- .../parts/debug/browser/loadedScriptsView.ts | 16 ++++--- .../debug/electron-browser/callStackView.ts | 43 +++++++++++-------- .../debug/electron-browser/debugHover.ts | 2 +- .../parts/debug/electron-browser/repl.ts | 29 ++++++++----- .../debug/electron-browser/variablesView.ts | 16 ++++--- .../electron-browser/watchExpressionsView.ts | 14 +++--- .../debug/test/browser/baseDebugView.test.ts | 18 ++++---- 9 files changed, 98 insertions(+), 70 deletions(-) diff --git a/src/vs/workbench/parts/debug/browser/baseDebugView.ts b/src/vs/workbench/parts/debug/browser/baseDebugView.ts index 3edeaf4a5cc..c073bb18876 100644 --- a/src/vs/workbench/parts/debug/browser/baseDebugView.ts +++ b/src/vs/workbench/parts/debug/browser/baseDebugView.ts @@ -14,6 +14,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; export const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; export const twistiePixels = 20; @@ -33,6 +35,7 @@ export interface IVariableTemplateData { expression: HTMLElement; name: HTMLElement; value: HTMLElement; + label: HighlightedLabel; } export function renderViewTree(container: HTMLElement): HTMLElement { @@ -88,11 +91,16 @@ export function renderExpressionValue(expressionOrValue: IExpression | string, c } } -export function renderVariable(variable: Variable, data: IVariableTemplateData, showChanged: boolean): void { +export function renderVariable(variable: Variable, data: IVariableTemplateData, showChanged: boolean, highlights: IHighlight[]): void { if (variable.available) { - data.name.textContent = replaceWhitespace(variable.name); - data.name.title = variable.type ? variable.type : variable.name; + let text = replaceWhitespace(variable.name); + if (variable.value && typeof variable.name === 'string') { + text += ':'; + } + data.label.set(text, highlights, variable.type ? variable.type : variable.name); dom.toggleClass(data.name, 'virtual', !!variable.presentationHint && variable.presentationHint.kind === 'virtual'); + } else if (variable.value && typeof variable.name === 'string') { + data.label.set(':'); } renderExpressionValue(variable, data.value, { @@ -102,9 +110,6 @@ export function renderVariable(variable: Variable, data: IVariableTemplateData, showHover: true, colorize: true }); - if (variable.value && typeof variable.name === 'string') { - data.name.textContent += ':'; - } } export interface IInputBoxOptions { @@ -122,9 +127,10 @@ export interface IExpressionTemplateData { inputBoxContainer: HTMLElement; enableInputBox(expression: IExpression, options: IInputBoxOptions); toDispose: IDisposable[]; + label: HighlightedLabel; } -export abstract class AbstractExpressionsRenderer implements ITreeRenderer { +export abstract class AbstractExpressionsRenderer implements ITreeRenderer { constructor( @IDebugService protected debugService: IDebugService, @@ -139,6 +145,8 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer { @@ -196,16 +204,16 @@ export abstract class AbstractExpressionsRenderer implements ITreeRenderer, index: number, data: IExpressionTemplateData): void { + renderElement(node: ITreeNode, index: number, data: IExpressionTemplateData): void { const { element } = node; if (element === this.debugService.getViewModel().getSelectedExpression()) { data.enableInputBox(element, this.getInputBoxOptions(element)); } else { - this.renderExpression(element, data); + this.renderExpression(element, data, createMatches(node.filterData)); } } - protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData): void; + protected abstract renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void; protected abstract getInputBoxOptions(expression: IExpression): IInputBoxOptions; disposeTemplate(templateData: IExpressionTemplateData): void { diff --git a/src/vs/workbench/parts/debug/browser/debugActions.ts b/src/vs/workbench/parts/debug/browser/debugActions.ts index 2eb0c164f12..c00d6590dc7 100644 --- a/src/vs/workbench/parts/debug/browser/debugActions.ts +++ b/src/vs/workbench/parts/debug/browser/debugActions.ts @@ -803,7 +803,7 @@ export class ReverseContinueAction extends AbstractDebugAction { } export class ReplCollapseAllAction extends CollapseAction2 { - constructor(tree: AsyncDataTree, private toFocus: { focus(): void; }) { + constructor(tree: AsyncDataTree, private toFocus: { focus(): void; }) { super(tree, true, undefined); } diff --git a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts index c59a90734ca..9379dbf7926 100644 --- a/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/parts/debug/browser/loadedScriptsView.ts @@ -33,6 +33,7 @@ import { WorkbenchAsyncDataTree, IListService, TreeResourceNavigator2 } from 'vs import { IThemeService } from 'vs/platform/theme/common/themeService'; import { DebugContentProvider } from 'vs/workbench/parts/debug/browser/debugContentProvider'; import { dispose } from 'vs/base/common/lifecycle'; +import { createMatches, FuzzyScore } from 'vs/base/common/filters'; const SMART = true; @@ -363,7 +364,7 @@ export class LoadedScriptsView extends ViewletPanel { private treeContainer: HTMLElement; private loadedScriptsItemType: IContextKey; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private treeLabels: ResourceLabels; private changeScheduler: RunOnceScheduler; private treeNeedsRefreshOnVisible: boolean; @@ -540,7 +541,7 @@ interface ILoadedScriptsItemTemplateData { label: IResourceLabel; } -class LoadedScriptsRenderer implements ITreeRenderer { +class LoadedScriptsRenderer implements ITreeRenderer { static readonly ID = 'lsrenderer'; @@ -555,11 +556,11 @@ class LoadedScriptsRenderer implements ITreeRenderer, index: number, data: ILoadedScriptsItemTemplateData): void { + renderElement(node: ITreeNode, index: number, data: ILoadedScriptsItemTemplateData): void { const element = node.element; @@ -589,6 +590,7 @@ class LoadedScriptsRenderer implements ITreeRenderer { +class LoadedScriptsFilter implements ITreeFilter { private filterText: string; @@ -630,7 +632,7 @@ class LoadedScriptsFilter implements ITreeFilter { this.filterText = filterText; } - filter(element: BaseTreeItem, parentVisibility: TreeVisibility): TreeFilterResult { + filter(element: BaseTreeItem, parentVisibility: TreeVisibility): TreeFilterResult { if (!this.filterText) { return TreeVisibility.Visible; @@ -645,4 +647,4 @@ class LoadedScriptsFilter implements ITreeFilter { } return TreeVisibility.Recurse; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index a4dfb9bea3c..91e17036d60 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -31,6 +31,8 @@ import { ITreeRenderer, ITreeNode, ITreeContextMenuEvent, IAsyncDataSource } fro import { TreeResourceNavigator2, WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { createMatches, FuzzyScore } from 'vs/base/common/filters'; const $ = dom.$; @@ -46,7 +48,7 @@ export class CallStackView extends ViewletPanel { private ignoreFocusStackFrameEvent: boolean; private callStackItemType: IContextKey; private dataSource: CallStackDataSource; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private contributedContextMenu: IMenu; constructor( @@ -328,6 +330,7 @@ interface IThreadTemplateData { name: HTMLElement; state: HTMLElement; stateLabel: HTMLSpanElement; + label: HighlightedLabel; } interface ISessionTemplateData { @@ -335,6 +338,7 @@ interface ISessionTemplateData { name: HTMLElement; state: HTMLElement; stateLabel: HTMLSpanElement; + label: HighlightedLabel; } interface IErrorTemplateData { @@ -347,13 +351,13 @@ interface ILabelTemplateData { interface IStackFrameTemplateData { stackFrame: HTMLElement; - label: HTMLElement; file: HTMLElement; fileName: HTMLElement; lineNumber: HTMLElement; + label: HighlightedLabel; } -class SessionsRenderer implements ITreeRenderer { +class SessionsRenderer implements ITreeRenderer { static readonly ID = 'session'; get templateId(): string { @@ -366,14 +370,15 @@ class SessionsRenderer implements ITreeRenderer, index: number, data: ISessionTemplateData): void { + renderElement(element: ITreeNode, index: number, data: ISessionTemplateData): void { const session = element.element; data.session.title = nls.localize({ key: 'session', comment: ['Session is a noun'] }, "Session"); - data.name.textContent = session.getLabel(); + data.label.set(session.getLabel(), createMatches(element.filterData)); const stoppedThread = session.getAllThreads().filter(t => t.stopped).pop(); data.stateLabel.textContent = stoppedThread ? nls.localize('paused', "Paused") @@ -385,7 +390,7 @@ class SessionsRenderer implements ITreeRenderer { +class ThreadsRenderer implements ITreeRenderer { static readonly ID = 'thread'; get templateId(): string { @@ -402,10 +407,10 @@ class ThreadsRenderer implements ITreeRenderer, index: number, data: IThreadTemplateData): void { + renderElement(element: ITreeNode, index: number, data: IThreadTemplateData): void { const thread = element.element; data.thread.title = nls.localize('thread', "Thread"); - data.name.textContent = thread.name; + data.label.set(thread.name, createMatches(element.filterData)); if (thread.stopped) { data.stateLabel.textContent = thread.stoppedDetails.description || @@ -420,7 +425,7 @@ class ThreadsRenderer implements ITreeRenderer { +class StackFramesRenderer implements ITreeRenderer { static readonly ID = 'stackFrame'; constructor(@ILabelService private readonly labelService: ILabelService) { } @@ -432,16 +437,17 @@ class StackFramesRenderer implements ITreeRenderer, index: number, data: IStackFrameTemplateData): void { + renderElement(element: ITreeNode, index: number, data: IStackFrameTemplateData): void { const stackFrame = element.element; dom.toggleClass(data.stackFrame, 'disabled', !stackFrame.source || !stackFrame.source.available || stackFrame.source.presentationHint === 'deemphasize'); dom.toggleClass(data.stackFrame, 'label', stackFrame.presentationHint === 'label'); @@ -451,8 +457,7 @@ class StackFramesRenderer implements ITreeRenderer { +class ErrorsRenderer implements ITreeRenderer { static readonly ID = 'error'; get templateId(): string { @@ -484,7 +489,7 @@ class ErrorsRenderer implements ITreeRenderer return data; } - renderElement(element: ITreeNode, index: number, data: IErrorTemplateData): void { + renderElement(element: ITreeNode, index: number, data: IErrorTemplateData): void { const error = element.element; data.label.textContent = error; data.label.title = error; @@ -495,7 +500,7 @@ class ErrorsRenderer implements ITreeRenderer } } -class LoadMoreRenderer implements ITreeRenderer { +class LoadMoreRenderer implements ITreeRenderer { static readonly ID = 'loadMore'; static readonly LABEL = nls.localize('loadMoreStackFrames', "Load More Stack Frames"); @@ -510,7 +515,7 @@ class LoadMoreRenderer implements ITreeRenderer, index: number, data: ILabelTemplateData): void { + renderElement(element: ITreeNode, index: number, data: ILabelTemplateData): void { data.label.textContent = LoadMoreRenderer.LABEL; } @@ -519,7 +524,7 @@ class LoadMoreRenderer implements ITreeRenderer { +class ShowMoreRenderer implements ITreeRenderer { static readonly ID = 'showMore'; get templateId(): string { @@ -533,7 +538,7 @@ class ShowMoreRenderer implements ITreeRenderer, index: number, data: ILabelTemplateData): void { + renderElement(element: ITreeNode, index: number, data: ILabelTemplateData): void { const stackFrames = element.element; if (stackFrames.every(sf => sf.source && sf.source.origin && sf.source.origin === stackFrames[0].source.origin)) { data.label.textContent = nls.localize('showMoreAndOrigin', "Show {0} More: {1}", stackFrames.length, stackFrames[0].source.origin); diff --git a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts index 061d9a72a1b..788457f386f 100644 --- a/src/vs/workbench/parts/debug/electron-browser/debugHover.ts +++ b/src/vs/workbench/parts/debug/electron-browser/debugHover.ts @@ -45,7 +45,7 @@ export class DebugHoverWidget implements IContentWidget { private _isVisible: boolean; private domNode: HTMLElement; - private tree: AsyncDataTree; + private tree: AsyncDataTree; private showAtPosition: Position; private highlightDecorations: string[]; private complexValueContainer: HTMLElement; diff --git a/src/vs/workbench/parts/debug/electron-browser/repl.ts b/src/vs/workbench/parts/debug/electron-browser/repl.ts index 45f77ae9975..21b493db790 100644 --- a/src/vs/workbench/parts/debug/electron-browser/repl.ts +++ b/src/vs/workbench/parts/debug/electron-browser/repl.ts @@ -66,6 +66,8 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; const $ = dom.$; @@ -81,7 +83,7 @@ interface IPrivateReplService { clearRepl(): void; } -function revealLastElement(tree: WorkbenchAsyncDataTree) { +function revealLastElement(tree: WorkbenchAsyncDataTree) { tree.scrollTop = tree.scrollHeight - tree.renderHeight; } @@ -95,7 +97,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati private static readonly REPL_INPUT_MAX_HEIGHT = 170; private history: HistoryNavigator; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; private replDelegate: ReplDelegate; private container: HTMLElement; private treeContainer: HTMLElement; @@ -250,7 +252,7 @@ export class Repl extends Panel implements IPrivateReplService, IHistoryNavigati getVisibleContent(): string { let text = ''; const lineDelimiter = this.textResourcePropertiesService.getEOL(this.model.uri); - const traverseAndAppend = (node: ITreeNode) => { + const traverseAndAppend = (node: ITreeNode) => { node.children.forEach(child => { text += child.element.toString() + lineDelimiter; if (!child.collapsed && child.children.length) { @@ -507,6 +509,7 @@ interface IExpressionTemplateData { output: HTMLElement; value: HTMLElement; annotation: HTMLElement; + label: HighlightedLabel; } interface ISimpleReplElementTemplateData { @@ -515,6 +518,7 @@ interface ISimpleReplElementTemplateData { source: HTMLElement; getReplElementSource(): IReplElementSource; toDispose: IDisposable[]; + label: HighlightedLabel; } interface IRawObjectReplTemplateData { @@ -523,9 +527,10 @@ interface IRawObjectReplTemplateData { name: HTMLElement; value: HTMLElement; annotation: HTMLElement; + label: HighlightedLabel; } -class ReplExpressionsRenderer implements ITreeRenderer { +class ReplExpressionsRenderer implements ITreeRenderer { static readonly ID = 'expressionRepl'; get templateId(): string { @@ -536,6 +541,7 @@ class ReplExpressionsRenderer implements ITreeRenderer, index: number, templateData: IExpressionTemplateData): void { + renderElement(element: ITreeNode, index: number, templateData: IExpressionTemplateData): void { const expression = element.element; - templateData.input.textContent = expression.name; + templateData.label.set(expression.name, createMatches(element.filterData)); renderExpressionValue(expression, templateData.value, { preserveWhitespace: !expression.hasChildren, showHover: false, @@ -562,7 +568,7 @@ class ReplExpressionsRenderer implements ITreeRenderer { +class ReplSimpleElementsRenderer implements ITreeRenderer { static readonly ID = 'simpleReplElement'; constructor( @@ -606,7 +612,7 @@ class ReplSimpleElementsRenderer implements ITreeRenderer, index: number, templateData: ISimpleReplElementTemplateData): void { + renderElement({ element }: ITreeNode, index: number, templateData: ISimpleReplElementTemplateData): void { // value dom.clearNode(templateData.value); // Reset classes to clear ansi decorations since templates are reused @@ -625,7 +631,7 @@ class ReplSimpleElementsRenderer implements ITreeRenderer { +class ReplRawObjectsRenderer implements ITreeRenderer { static readonly ID = 'rawObject'; get templateId(): string { @@ -639,14 +645,17 @@ class ReplRawObjectsRenderer implements ITreeRenderer, index: number, templateData: IRawObjectReplTemplateData): void { + renderElement(node: ITreeNode, index: number, templateData: IRawObjectReplTemplateData): void { // key + const element = node.element; + templateData.label.set(element.name ? `${element.name}:` : '', createMatches(node.filterData)); if (element.name) { templateData.name.textContent = `${element.name}:`; } else { diff --git a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts index 9540ae417cd..b6b470d6697 100644 --- a/src/vs/workbench/parts/debug/electron-browser/variablesView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/variablesView.ts @@ -28,6 +28,8 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/listService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { FuzzyScore, createMatches } from 'vs/base/common/filters'; +import { HighlightedLabel, IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; const $ = dom.$; @@ -37,7 +39,7 @@ export class VariablesView extends ViewletPanel { private onFocusStackFrameScheduler: RunOnceScheduler; private needsRefresh: boolean; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; constructor( options: IViewletViewOptions, @@ -178,6 +180,7 @@ export class VariablesDataSource implements IAsyncDataSource { @@ -198,7 +201,7 @@ class VariablesDelegate implements IListVirtualDelegate { } } -class ScopesRenderer implements ITreeRenderer { +class ScopesRenderer implements ITreeRenderer { static readonly ID = 'scope'; @@ -209,12 +212,13 @@ class ScopesRenderer implements ITreeRenderer renderTemplate(container: HTMLElement): IScopeTemplateData { let data: IScopeTemplateData = Object.create(null); data.name = dom.append(container, $('.scope')); + data.label = new HighlightedLabel(data.name, false); return data; } - renderElement(element: ITreeNode, index: number, templateData: IScopeTemplateData): void { - templateData.name.textContent = element.element.name; + renderElement(element: ITreeNode, index: number, templateData: IScopeTemplateData): void { + templateData.label.set(element.element.name, createMatches(element.filterData)); } disposeTemplate(templateData: IScopeTemplateData): void { @@ -230,8 +234,8 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { return VariablesRenderer.ID; } - protected renderExpression(expression: IExpression, data: IExpressionTemplateData): void { - renderVariable(expression as Variable, data, true); + protected renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void { + renderVariable(expression as Variable, data, true, highlights); } protected getInputBoxOptions(expression: IExpression): IInputBoxOptions { diff --git a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts index 7e461ac86a3..24c330faa89 100644 --- a/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/watchExpressionsView.ts @@ -30,6 +30,8 @@ import { IAsyncDataSource, ITreeMouseEvent, ITreeContextMenuEvent, ITreeDragAndD import { IDragAndDropData } from 'vs/base/browser/dnd'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { IHighlight } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; @@ -37,7 +39,7 @@ export class WatchExpressionsView extends ViewletPanel { private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh: boolean; - private tree: WorkbenchAsyncDataTree; + private tree: WorkbenchAsyncDataTree; constructor( options: IViewletViewOptions, @@ -223,8 +225,9 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer { return WatchExpressionsRenderer.ID; } - protected renderExpression(expression: IExpression, data: IExpressionTemplateData): void { - data.name.textContent = expression.name; + protected renderExpression(expression: IExpression, data: IExpressionTemplateData, highlights: IHighlight[]): void { + const text = typeof expression.value === 'string' ? `${expression.name}:` : expression.name; + data.label.set(text, highlights, expression.type ? expression.type : expression.value); renderExpressionValue(expression, data.value, { showChanged: true, maxValueLength: MAX_VALUE_RENDER_LENGTH_IN_VIEWLET, @@ -232,11 +235,6 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer { showHover: true, colorize: true }); - data.name.title = expression.type ? expression.type : expression.value; - - if (typeof expression.value === 'string') { - data.name.textContent += ':'; - } } protected getInputBoxOptions(expression: IExpression): IInputBoxOptions { diff --git a/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts b/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts index 837cb46a9b6..e90ea964925 100644 --- a/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/parts/debug/test/browser/baseDebugView.test.ts @@ -8,6 +8,7 @@ import { replaceWhitespace, renderExpressionValue, renderVariable } from 'vs/wor import * as dom from 'vs/base/browser/dom'; import { Expression, Variable, Scope, StackFrame, Thread } from 'vs/workbench/parts/debug/common/debugModel'; import { MockSession } from 'vs/workbench/parts/debug/test/common/mockDebug'; +import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; const $ = dom.$; suite('Debug - Base Debug View', () => { @@ -61,9 +62,10 @@ suite('Debug - Base Debug View', () => { let expression = $('.'); let name = $('.'); let value = $('.'); - renderVariable(variable, { expression, name, value }, false); + let label = new HighlightedLabel(name, false); + renderVariable(variable, { expression, name, value, label }, false, []); - assert.equal(name.textContent, 'foo'); + assert.equal(label.element.textContent, 'foo'); assert.equal(value.textContent, ''); assert.equal(value.title, ''); @@ -71,19 +73,19 @@ suite('Debug - Base Debug View', () => { expression = $('.'); name = $('.'); value = $('.'); - renderVariable(variable, { expression, name, value }, false); + renderVariable(variable, { expression, name, value, label }, false, []); assert.equal(value.textContent, 'hey'); - assert.equal(name.textContent, 'foo:'); - assert.equal(name.title, 'string'); + assert.equal(label.element.textContent, 'foo:'); + assert.equal(label.element.title, 'string'); variable = new Variable(session, scope, 2, 'console', 'console', '5', 0, 0, { kind: 'virtual' }); expression = $('.'); name = $('.'); value = $('.'); - renderVariable(variable, { expression, name, value }, false); + renderVariable(variable, { expression, name, value, label }, false, []); assert.equal(name.className, 'virtual'); - assert.equal(name.textContent, 'console:'); - assert.equal(name.title, 'console'); + assert.equal(label.element.textContent, 'console:'); + assert.equal(label.element.title, 'console'); assert.equal(value.className, 'value number'); }); }); From 191c11b888d6e644d07192ac95b864e08b304032 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 16:17:53 +0100 Subject: [PATCH 0899/1066] highglightLabel is not disposable --- .../base/browser/ui/highlightedlabel/highlightedLabel.ts | 8 +------- src/vs/base/browser/ui/iconLabel/iconLabel.ts | 5 ++--- src/vs/base/parts/quickopen/browser/quickOpenModel.ts | 1 - src/vs/base/test/browser/highlightedLabel.test.ts | 4 ---- src/vs/editor/contrib/documentSymbols/outlineTree.ts | 3 +-- .../parts/markers/electron-browser/markersTreeViewer.ts | 8 ++------ 6 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 14d7439bf90..836818294c2 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import * as objects from 'vs/base/common/objects'; import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; @@ -14,7 +13,7 @@ export interface IHighlight { end: number; } -export class HighlightedLabel implements IDisposable { +export class HighlightedLabel { private domNode: HTMLElement; private text: string; @@ -91,11 +90,6 @@ export class HighlightedLabel implements IDisposable { this.didEverRender = true; } - dispose() { - this.text = null!; // StrictNullOverride: nulling out ok in dispose - this.highlights = null!; // StrictNullOverride: nulling out ok in dispose - } - static escapeNewLines(text: string, highlights: IHighlight[]): string { let total = 0; diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index be99067ee1d..a61b1bba525 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -100,13 +100,13 @@ export class IconLabel extends Disposable { this.labelDescriptionContainer = this._register(new FastLabelNode(dom.append(this.domNode.element, dom.$('.monaco-icon-label-description-container')))); if (options && options.supportHighlights) { - this.labelNode = this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons)); + this.labelNode = new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')), !options.donotSupportOcticons); } else { this.labelNode = this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('a.label-name')))); } if (options && options.supportDescriptionHighlights) { - this.descriptionNodeFactory = () => this._register(new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons)); + this.descriptionNodeFactory = () => new HighlightedLabel(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')), !options.donotSupportOcticons); } else { this.descriptionNodeFactory = () => this._register(new FastLabelNode(dom.append(this.labelDescriptionContainer.element, dom.$('span.label-description')))); } @@ -157,4 +157,3 @@ export class IconLabel extends Disposable { } } } - diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 5780dee88ca..4959a71f185 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -485,7 +485,6 @@ class Renderer implements IRenderer { data.container = null; data.entry = null; data.keybinding = null; - data.detail.dispose(); data.detail = null; data.group = null; data.icon = null; diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 4fbab5b9614..c658c835c04 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -12,10 +12,6 @@ suite('HighlightedLabel', () => { label = new HighlightedLabel(document.createElement('div'), true); }); - teardown(() => { - label.dispose(); - }); - test('empty label', function () { assert.equal(label.element.innerHTML, ''); }); diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 3daabc1627d..4c529be6e97 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -240,9 +240,8 @@ export class OutlineRenderer implements IRenderer { }; disposeTemplate(tree: ITree, templateId: string, template: OutlineTemplate): void { - template.label.dispose(); + // noop } - } export class OutlineTreeState { diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index a284d526c5e..e7cb8ee2bbd 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -305,7 +305,6 @@ class MarkerWidget extends Disposable { lastLineElement = dom.append(messageContainer, dom.$('.marker-message-line')); const highlightedLabel = new HighlightedLabel(lastLineElement, false); highlightedLabel.set(lines[index], lineMatches[index]); - this.disposables.push(highlightedLabel); } this.renderDetails(marker, filterData, multiline ? lastLineElement : this.messageAndDetailsContainer); } @@ -325,8 +324,6 @@ class MarkerWidget extends Disposable { const lnCol = dom.append(parent, dom.$('span.marker-line')); lnCol.textContent = Messages.MARKERS_PANEL_AT_LINE_COL_NUMBER(marker.startLineNumber, marker.startColumn); - - this.disposables.push(...[source, code]); } private static iconClassNameFor(element: IMarker): string { @@ -382,8 +379,7 @@ export class RelatedInformationRenderer implements ITreeRenderer Date: Thu, 31 Jan 2019 17:47:14 +0100 Subject: [PATCH 0900/1066] fixes #66899 --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 ++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 ++ .../workbench/browser/actions/listCommands.ts | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d7362e7e14d..5ee6f822611 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -831,6 +831,8 @@ export abstract class AbstractTree implements IDisposable private _onWillRefilter = new Emitter(); readonly onWillRefilter: Event = this._onWillRefilter.event; + get filterOnType(): boolean { return !!this._options.filterOnType; } + get onDidDispose(): Event { return this.view.onDidDispose; } constructor( diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 3b50d3ff5f8..c0fe25efda9 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -284,6 +284,8 @@ export class AsyncDataTree implements IDisposable get onDidFocus(): Event { return this.tree.onDidFocus; } get onDidBlur(): Event { return this.tree.onDidBlur; } + get filterOnType(): boolean { return this.tree.filterOnType; } + get onDidDispose(): Event { return this.tree.onDidDispose; } constructor( diff --git a/src/vs/workbench/browser/actions/listCommands.ts b/src/vs/workbench/browser/actions/listCommands.ts index 087f1f5f2aa..2ce44cacbb7 100644 --- a/src/vs/workbench/browser/actions/listCommands.ts +++ b/src/vs/workbench/browser/actions/listCommands.ts @@ -762,3 +762,21 @@ CommandsRegistry.registerCommand({ } } }); + +CommandsRegistry.registerCommand({ + id: 'list.toggleFilterOnType', + handler: (accessor) => { + const focused = accessor.get(IListService).lastFocusedList; + + // List + if (focused instanceof List || focused instanceof PagedList) { + // TODO@joao + } + + // ObjectTree + else if (focused instanceof ObjectTree || focused instanceof DataTree || focused instanceof AsyncDataTree) { + const tree = focused; + tree.updateOptions({ filterOnType: !tree.filterOnType }); + } + } +}); From b3ebb15a5f156e9f27185d1672b7b7b2d9daf873 Mon Sep 17 00:00:00 2001 From: isidor Date: Thu, 31 Jan 2019 18:12:49 +0100 Subject: [PATCH 0901/1066] explorer: select parent folder when new file / new folder canceled fixes #67653 fixes #66475 --- src/vs/workbench/parts/files/electron-browser/fileActions.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/parts/files/electron-browser/fileActions.ts b/src/vs/workbench/parts/files/electron-browser/fileActions.ts index f38833fd7cd..420a845ae97 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.ts @@ -145,6 +145,8 @@ export class NewFileAction extends BaseErrorReportingAction { this.explorerService.setEditable(stat, null); if (success) { onSuccess(value); + } else { + this.explorerService.select(folder.resource).then(undefined, onUnexpectedError); } } }); @@ -199,6 +201,8 @@ export class NewFolderAction extends BaseErrorReportingAction { this.explorerService.setEditable(stat, null); if (success) { onSuccess(value); + } else { + this.explorerService.select(folder.resource).then(undefined, onUnexpectedError); } } }); From ef0f902787952638317d709e0c1c5030b47a79ac Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 31 Jan 2019 10:44:46 -0800 Subject: [PATCH 0902/1066] Fix custom data capitalization --- extensions/css-language-features/server/src/customData.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/css-language-features/server/src/customData.ts b/extensions/css-language-features/server/src/customData.ts index b5cbdc27c7a..f173d884a2b 100644 --- a/extensions/css-language-features/server/src/customData.ts +++ b/extensions/css-language-features/server/src/customData.ts @@ -43,8 +43,8 @@ function parseCSSData(source: string): CSSDataV1 { return { version: 1, properties: rawData.properties || [], - atDirectives: rawData.atdirectives || [], - pseudoClasses: rawData.pseudoclasses || [], - pseudoElements: rawData.pseudoelements || [] + atDirectives: rawData.atDirectives || [], + pseudoClasses: rawData.pseudoClasses || [], + pseudoElements: rawData.pseudoElements || [] }; } From 7debb7ebdc1e4bdb193d9a91753a8e1242ccff8a Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 31 Jan 2019 11:30:24 -0800 Subject: [PATCH 0903/1066] listen to onDidVisibleEditors change as well refs #67356 --- src/vs/workbench/electron-browser/workbench.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index f222c743e7d..d82db96296d 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -581,6 +581,10 @@ export class Workbench extends Disposable implements IPartService { this.closeEmptyWindowScheduler.schedule(); } } + + if (this.editorHidden) { + this.setEditorHidden(false); + } } private onAllEditorsClosed(): void { From e795093995c7382d0fb0f2e126f6f91aa81637c2 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 31 Jan 2019 11:42:34 -0800 Subject: [PATCH 0904/1066] adjust titlebar size with zoom appropriately --- 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 c6bd885de3b..4b3eb7bdb87 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 = isMacintosh ? 22 : 30; - maximumHeight: number = isMacintosh ? 22 : 30; + get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); } + get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); } private _onDidChange = new Emitter<{ width: number; height: number; }>(); readonly onDidChange = this._onDidChange.event; From 9c887c95738845aca072cbb19702a49e13ff325f Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 21:12:43 +0100 Subject: [PATCH 0905/1066] proper tree aria-posinset and aria-setsize attributes fixes #67156 --- src/vs/base/browser/ui/tree/abstractTree.ts | 27 ++++--- src/vs/base/browser/ui/tree/asyncDataTree.ts | 2 + src/vs/base/browser/ui/tree/indexTreeModel.ts | 76 +++++++++++++++++-- src/vs/base/browser/ui/tree/tree.ts | 2 + 4 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5ee6f822611..6d472170250 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -171,8 +171,9 @@ export class ComposedTreeDelegate implements IListV } interface ITreeListTemplateData { - twistie: HTMLElement; - templateData: T; + readonly container: HTMLElement; + readonly twistie: HTMLElement; + readonly templateData: T; } interface ITreeRendererOptions { @@ -218,7 +219,7 @@ class TreeRenderer implements IListRenderer, index: number, templateData: ITreeListTemplateData): void { @@ -227,7 +228,9 @@ class TreeRenderer implements IListRenderer implements IListRenderer, twistieElement: HTMLElement) { + private update(node: ITreeNode, templateData: ITreeListTemplateData) { if (this.renderer.renderTwistie) { - this.renderer.renderTwistie(node.element, twistieElement); + this.renderer.renderTwistie(node.element, templateData.twistie); } - toggleClass(twistieElement, 'collapsible', node.collapsible); - toggleClass(twistieElement, 'collapsed', node.collapsible && node.collapsed); + toggleClass(templateData.twistie, 'collapsible', node.collapsible); + toggleClass(templateData.twistie, 'collapsed', node.collapsible && node.collapsed); + + if (node.collapsible) { + templateData.container.setAttribute('aria-expanded', String(!node.collapsed)); + } else { + templateData.container.removeAttribute('aria-expanded'); + } } dispose(): void { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index c0fe25efda9..332fa1d0bcf 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -59,6 +59,8 @@ class AsyncDataTreeNodeWrapper implements ITreeNode | undefined { return this.node.parent && new AsyncDataTreeNodeWrapper(this.node.parent); } get children(): ITreeNode[] { return this.node.children.map(node => new AsyncDataTreeNodeWrapper(node)); } get depth(): number { return this.node.depth; } + get visibleChildrenCount(): number { return this.node.visibleChildrenCount; } + get visibleChildIndex(): number { return this.node.visibleChildIndex; } get collapsible(): boolean { return this.node.collapsible; } get collapsed(): boolean { return this.node.collapsed; } get visible(): boolean { return this.node.visible; } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index cc6555950aa..b22af463d96 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -12,6 +12,8 @@ import { ISpliceable } from 'vs/base/common/sequence'; interface IMutableTreeNode extends ITreeNode { readonly parent: IMutableTreeNode | undefined; readonly children: IMutableTreeNode[]; + visibleChildrenCount: number; + visibleChildIndex: number; collapsible: boolean; collapsed: boolean; renderNodeCount: number; @@ -76,6 +78,8 @@ export class IndexTreeModel, TFilterData = voi element: rootElement, children: [], depth: 0, + visibleChildrenCount: 0, + visibleChildIndex: -1, collapsible: false, collapsed: false, renderNodeCount: 0, @@ -99,17 +103,59 @@ export class IndexTreeModel, TFilterData = voi const treeListElementsToInsert: ITreeNode[] = []; const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); + const lastIndex = location[location.length - 1]; + + // figure out what's the visible child start index right before the + // splice point + let visibleChildStartIndex = 0; + + for (let i = lastIndex; i >= 0 && i < parentNode.children.length; i--) { + const child = parentNode.children[i]; + + if (child.visible) { + visibleChildStartIndex = child.visibleChildIndex; + break; + } + } + const nodesToInsert: IMutableTreeNode[] = []; + let insertedVisibleChildrenCount = 0; let renderNodeCount = 0; - Iterator.forEach(nodesToInsertIterator, node => { - nodesToInsert.push(node); - renderNodeCount += node.renderNodeCount; + Iterator.forEach(nodesToInsertIterator, child => { + nodesToInsert.push(child); + renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++; + } }); - const lastIndex = location[location.length - 1]; const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); + // figure out what is the count of deleted visible children + let deletedVisibleChildrenCount = 0; + + for (const child of deletedNodes) { + if (child.visible) { + deletedVisibleChildrenCount++; + } + } + + // and adjust for all visible children after the splice point + if (deletedVisibleChildrenCount !== 0) { + for (let i = lastIndex + nodesToInsert.length; i < parentNode.children.length; i++) { + const child = parentNode.children[i]; + + if (child.visible) { + child.visibleChildIndex -= deletedVisibleChildrenCount; + } + } + } + + // update parent's visible children count + parentNode.visibleChildrenCount += insertedVisibleChildrenCount - deletedVisibleChildrenCount; + if (revealed && visible) { const visibleDeleteCount = deletedNodes.reduce((r, node) => r + node.renderNodeCount, 0); @@ -253,6 +299,8 @@ export class IndexTreeModel, TFilterData = voi element: treeElement.element, children: [], depth: parent.depth + 1, + visibleChildrenCount: 0, + visibleChildIndex: -1, collapsible: typeof treeElement.collapsible === 'boolean' ? treeElement.collapsible : (typeof treeElement.collapsed !== 'undefined'), collapsed: typeof treeElement.collapsed === 'undefined' ? this.collapseByDefault : treeElement.collapsed, renderNodeCount: 1, @@ -270,17 +318,21 @@ export class IndexTreeModel, TFilterData = voi const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed; const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); - let hasVisibleDescendants = false; + let visibleChildrenCount = 0; let renderNodeCount = 1; Iterator.forEach(childNodes, child => { node.children.push(child); - hasVisibleDescendants = hasVisibleDescendants || child.visible; renderNodeCount += child.renderNodeCount; + + if (child.visible) { + child.visibleChildIndex = visibleChildrenCount++; + } }); node.collapsible = node.collapsible || node.children.length > 0; - node.visible = visibility === TreeVisibility.Recurse ? hasVisibleDescendants : (visibility === TreeVisibility.Visible); + node.visibleChildrenCount = visibleChildrenCount; + node.visible = visibility === TreeVisibility.Recurse ? visibleChildrenCount > 0 : (visibility === TreeVisibility.Visible); if (!node.visible) { node.renderNodeCount = 0; @@ -358,9 +410,19 @@ export class IndexTreeModel, TFilterData = voi let hasVisibleDescendants = false; if (!node.collapsed || visibility! !== TreeVisibility.Hidden) { + let visibleChildIndex = 0; + for (const child of node.children) { hasVisibleDescendants = this._updateNodeAfterFilterChange(child, visibility!, result, revealed && !node.collapsed) || hasVisibleDescendants; + + if (child.visible) { + child.visibleChildIndex = visibleChildIndex++; + } } + + node.visibleChildrenCount = visibleChildIndex; + } else { + node.visibleChildrenCount = 0; } if (node !== this.root) { diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index 4880293c269..adde3a3f6f2 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -84,6 +84,8 @@ export interface ITreeNode { readonly parent: ITreeNode | undefined; readonly children: ITreeNode[]; readonly depth: number; + readonly visibleChildrenCount: number; + readonly visibleChildIndex: number; readonly collapsible: boolean; readonly collapsed: boolean; readonly visible: boolean; From d93d2c344091fd5f03552eee007814405d8caf33 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 21:17:17 +0100 Subject: [PATCH 0906/1066] fixes #67576 --- src/vs/base/browser/ui/tree/abstractTree.ts | 3 ++- src/vs/platform/list/browser/listService.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 6d472170250..5fa35f962d0 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -23,6 +23,7 @@ import { localize } from 'vs/nls'; import { disposableTimeout } from 'vs/base/common/async'; import { isMacintosh } from 'vs/base/common/platform'; import { values } from 'vs/base/common/map'; +import { clamp } from 'vs/base/common/numbers'; function asTreeDragAndDropData(data: IDragAndDropData): IDragAndDropData { if (data instanceof ElementsDragAndDropData) { @@ -206,7 +207,7 @@ class TreeRenderer implements IListRenderer { templateData.twistie.style.marginLeft = `${node.depth * this.indent}px`; diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 84f5114a9a9..e3ac00702cd 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1288,6 +1288,8 @@ configurationRegistry.registerConfiguration({ [treeIndentKey]: { 'type': 'number', 'default': 8, + minimum: 0, + maximum: 20, 'description': localize('tree indent setting', "Controls tree indentation in pixels.") }, [keyboardNavigationSettingKey]: { From 97688712b3614902b29d08b61f5afba4923e0eec Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 21:39:38 +0100 Subject: [PATCH 0907/1066] tree: respect multi select key fixes #67648 --- src/vs/base/browser/ui/list/listWidget.ts | 10 ++++++++-- src/vs/base/browser/ui/tree/abstractTree.ts | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ebe2af766dc..4d417ec11d9 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -505,7 +505,7 @@ const DefaultOpenController: IOpenController = { class MouseController implements IDisposable { private multipleSelectionSupport: boolean; - private multipleSelectionController: IMultipleSelectionController; + readonly multipleSelectionController: IMultipleSelectionController; private openController: IOpenController; private disposables: IDisposable[] = []; @@ -1063,6 +1063,11 @@ export class List implements ISpliceable, IDisposable { private spliceable: ISpliceable; private styleElement: HTMLStyleElement; private styleController: IStyleController; + private mouseController: MouseController | undefined; + + get multipleSelectionController(): IMultipleSelectionController { + return this.mouseController ? this.mouseController.multipleSelectionController : DefaultMultipleSelectionContoller; + } private _onDidUpdateOptions = new Emitter>(); readonly onDidUpdateOptions = this._onDidUpdateOptions.event; @@ -1208,7 +1213,8 @@ export class List implements ISpliceable, IDisposable { } if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) { - this.disposables.push(new MouseController(this, this.view, _options)); + this.mouseController = new MouseController(this, this.view, _options); + this.disposables.push(this.mouseController); } this.onFocusChange(this._onFocusChange, this, this.disposables); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5fa35f962d0..c68823146a3 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IListStyles, mightProducePrintableCharacter, isSelectionRangeChangeEvent, isSelectionSingleChangeEvent } from 'vs/base/browser/ui/list/listWidget'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; @@ -1127,7 +1127,8 @@ export abstract class AbstractTree implements IDisposable if (!node) { return; } - if (isSelectionRangeChangeEvent(e) || isSelectionSingleChangeEvent(e)) { + + if (this.view.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.view.multipleSelectionController.isSelectionSingleChangeEvent(e)) { return; } From ce755eaf650acdcca39ec315f5f37f166967b0df Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 21:43:41 +0100 Subject: [PATCH 0908/1066] remove specific type filter shadow color fixes #67446 --- src/vs/platform/theme/common/colorRegistry.ts | 1 - src/vs/platform/theme/common/styler.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index b253d48e95e..a60acb6546f 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -238,7 +238,6 @@ export const listWarningForeground = registerColor('list.warningForeground', { d export const listMatchesBackground = registerColor('list.matchesBackground', { light: '#efc1ad', dark: '#653723', hc: Color.black }, nls.localize('listMatchesBackground', 'Background color of the type filter widget in lists and trees.')); export const listMatchesOutline = registerColor('list.matchesOutline', { dark: Color.transparent, light: Color.transparent, hc: '#f38518' }, nls.localize('listMatchesOutline', 'Outline color of the type filter widget in lists and trees.')); export const listNoMatchesOutline = registerColor('list.noMatchesOutline', { dark: '#B89500', light: '#B89500', hc: contrastBorder }, nls.localize('listNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.')); -export const listMatchesShadow = registerColor('list.matchesShadow', { dark: '#000000', light: '#A8A8A8', hc: null }, nls.localize('listMatchesShadow', 'Shadow color of the type filter widget in lists and trees.')); export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#0066BF', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index e96a4270c33..f3f7a613fff 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listMatchesOutline, listNoMatchesOutline, listMatchesShadow, listMatchesBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listMatchesOutline, listNoMatchesOutline, listMatchesBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -252,7 +252,7 @@ export const defaultListStyles: IColorMapping = { listMatchesBackground, listMatchesOutline, listNoMatchesOutline, - listMatchesShadow + listMatchesShadow: widgetShadow }; export interface IButtonStyleOverrides extends IStyleOverrides { From 47cbad05abbd782ee1b9d1abd84033357e2a24c7 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Thu, 31 Jan 2019 21:58:37 +0100 Subject: [PATCH 0909/1066] fixes #67446 --- src/vs/base/browser/ui/list/listWidget.ts | 18 +++++++++--------- src/vs/platform/theme/common/colorRegistry.ts | 6 +++--- src/vs/platform/theme/common/styler.ts | 14 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 4d417ec11d9..37ccdcbf94f 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -759,16 +759,16 @@ export class DefaultStyleController implements IStyleController { `); } - if (styles.listMatchesBackground) { - content.push(`.monaco-list-type-filter { background-color: ${styles.listMatchesBackground} }`); + if (styles.listFilterWidgetBackground) { + content.push(`.monaco-list-type-filter { background-color: ${styles.listFilterWidgetBackground} }`); } - if (styles.listMatchesOutline) { - content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listMatchesOutline}; }`); + if (styles.listFilterWidgetOutline) { + content.push(`.monaco-list-type-filter { border: 1px solid ${styles.listFilterWidgetOutline}; }`); } - if (styles.listNoMatchesOutline) { - content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listNoMatchesOutline}; }`); + if (styles.listFilterWidgetNoMatchesOutline) { + content.push(`.monaco-list-type-filter.no-matches { border: 1px solid ${styles.listFilterWidgetNoMatchesOutline}; }`); } if (styles.listMatchesShadow) { @@ -822,9 +822,9 @@ export interface IListStyles { listInactiveFocusOutline?: Color; listSelectionOutline?: Color; listHoverOutline?: Color; - listMatchesBackground?: Color; - listMatchesOutline?: Color; - listNoMatchesOutline?: Color; + listFilterWidgetBackground?: Color; + listFilterWidgetOutline?: Color; + listFilterWidgetNoMatchesOutline?: Color; listMatchesShadow?: Color; } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index a60acb6546f..8c94876cb23 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -235,9 +235,9 @@ export const listHighlightForeground = registerColor('list.highlightForeground', export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hc: '#B89500' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hc: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#4d9e4d', light: '#117711', hc: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); -export const listMatchesBackground = registerColor('list.matchesBackground', { light: '#efc1ad', dark: '#653723', hc: Color.black }, nls.localize('listMatchesBackground', 'Background color of the type filter widget in lists and trees.')); -export const listMatchesOutline = registerColor('list.matchesOutline', { dark: Color.transparent, light: Color.transparent, hc: '#f38518' }, nls.localize('listMatchesOutline', 'Outline color of the type filter widget in lists and trees.')); -export const listNoMatchesOutline = registerColor('list.noMatchesOutline', { dark: '#B89500', light: '#B89500', hc: contrastBorder }, nls.localize('listNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.')); +export const listFilterWidgetBackground = registerColor('listFilterWidget.background', { light: '#efc1ad', dark: '#653723', hc: Color.black }, nls.localize('listFilterWidgetBackground', 'Background color of the type filter widget in lists and trees.')); +export const listFilterWidgetOutline = registerColor('listFilterWidget.outline', { dark: Color.transparent, light: Color.transparent, hc: '#f38518' }, nls.localize('listFilterWidgetOutline', 'Outline color of the type filter widget in lists and trees.')); +export const listFilterWidgetNoMatchesOutline = registerColor('listFilterWidget.noMatchesOutline', { dark: '#BE1100', light: '#BE1100', hc: contrastBorder }, nls.localize('listFilterWidgetNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.')); export const pickerGroupForeground = registerColor('pickerGroup.foreground', { dark: '#3794FF', light: '#0066BF', hc: Color.white }, nls.localize('pickerGroupForeground', "Quick picker color for grouping labels.")); export const pickerGroupBorder = registerColor('pickerGroup.border', { dark: '#3F3F46', light: '#CCCEDB', hc: Color.white }, nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index f3f7a613fff..d7725d4d1ca 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; -import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listMatchesOutline, listNoMatchesOutline, listMatchesBackground } from 'vs/platform/theme/common/colorRegistry'; +import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground } from 'vs/platform/theme/common/colorRegistry'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; @@ -223,9 +223,9 @@ export interface IListStyleOverrides extends IStyleOverrides { listInactiveFocusOutline?: ColorIdentifier; listSelectionOutline?: ColorIdentifier; listHoverOutline?: ColorIdentifier; - listMatchesBackground?: ColorIdentifier; - listMatchesOutline?: ColorIdentifier; - listNoMatchesOutline?: ColorIdentifier; + listFilterWidgetBackground?: ColorIdentifier; + listFilterWidgetOutline?: ColorIdentifier; + listFilterWidgetNoMatchesOutline?: ColorIdentifier; listMatchesShadow?: ColorIdentifier; } @@ -249,9 +249,9 @@ export const defaultListStyles: IColorMapping = { listFocusOutline: activeContrastBorder, listSelectionOutline: activeContrastBorder, listHoverOutline: activeContrastBorder, - listMatchesBackground, - listMatchesOutline, - listNoMatchesOutline, + listFilterWidgetBackground: listFilterWidgetBackground, + listFilterWidgetOutline: listFilterWidgetOutline, + listFilterWidgetNoMatchesOutline: listFilterWidgetNoMatchesOutline, listMatchesShadow: widgetShadow }; From ee10609110fab0de4cf98453ccd3e75272b9c4ec Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 31 Jan 2019 23:17:38 +0100 Subject: [PATCH 0910/1066] Fixes #67607: Don't trust tokens/states produced by the viewport tokenization --- src/vs/editor/common/model/textModel.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 44037d1b5b1..7893c700738 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1819,13 +1819,11 @@ export class TextModel extends Disposable implements model.ITextModel { let r = this._tokens._tokenizeText(this._buffer, text, state); if (r) { this._tokens._setTokens(this._tokens.languageIdentifier.id, i - 1, text.length, r.tokens); - /* - * we think it's valid and give it a state but we don't update `_invalidLineStartIndex` then the top-to-bottom tokenization - * goes through the viewport, it can skip them if they already have correct tokens and state, and the lines after the viewport - * can still be tokenized. - */ - this._tokens._setIsInvalid(i - 1, false); - this._tokens._setState(i - 1, state); + + // We cannot trust these states/tokens to be valid! + // (see https://github.com/Microsoft/vscode/issues/67607) + this._tokens._setIsInvalid(i - 1, true); + this._tokens._setState(i - 1, null); state = r.endState.clone(); eventBuilder.registerChangedTokens(i); } else { From 436e2f8122eae96fb34a6358c960d790ba3e13fe Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 31 Jan 2019 23:20:41 +0100 Subject: [PATCH 0911/1066] Fix strict null check (#67607) --- src/vs/editor/common/model/textModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 7893c700738..3a0d722fdea 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1823,7 +1823,7 @@ export class TextModel extends Disposable implements model.ITextModel { // We cannot trust these states/tokens to be valid! // (see https://github.com/Microsoft/vscode/issues/67607) this._tokens._setIsInvalid(i - 1, true); - this._tokens._setState(i - 1, null); + this._tokens._setState(i - 1, state); state = r.endState.clone(); eventBuilder.registerChangedTokens(i); } else { From 16e58d25d008976c8a36867f4b638c07f750794e Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Thu, 31 Jan 2019 14:41:03 -0800 Subject: [PATCH 0912/1066] Use default font-size for reply button --- .../comments/electron-browser/commentsEditorContribution.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts index f6cbca33e7a..e761197389b 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentsEditorContribution.ts @@ -708,7 +708,8 @@ registerThemingParticipant((theme, collector) => { ` color: ${monacoEditorForeground}` + `}` + `.monaco-editor .review-widget .body .comment-form .review-thread-reply-button {` + - ` color: ${monacoEditorForeground}` + + ` color: ${monacoEditorForeground};` + + ` font-size: inherit` + `}` ); } From c0dba426a2f4736a818bcaee21e9a34e2efcfb8a Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 31 Jan 2019 15:23:58 -0800 Subject: [PATCH 0913/1066] Fix Microsoft/vscode-pull-request-github#895. Comment editor should layout automatically. --- .../parts/comments/electron-browser/simpleCommentEditor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts index 779d0e58fe7..81a65c067a2 100644 --- a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts +++ b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts @@ -63,6 +63,7 @@ export class SimpleCommentEditor extends CodeEditorWidget { verticalHasArrows: false, horizontalHasArrows: false }, + automaticLayout: true, overviewRulerLanes: 2, lineDecorationsWidth: 0, scrollBeyondLastLine: false, From 3d2f7d26a061e4545d43d6185704a24d0750da40 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Thu, 31 Jan 2019 16:01:25 -0800 Subject: [PATCH 0914/1066] Fix Microsoft/vscode-pull-request-github#893. Avoid margin collapse --- .../parts/comments/electron-browser/commentThreadWidget.ts | 3 ++- .../parts/comments/electron-browser/media/review.css | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 2af96523bbe..456f7b9be20 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -622,8 +622,9 @@ export class ReviewZoneWidget extends ZoneWidget { const lineHeight = this.editor.getConfiguration().lineHeight; const arrowHeight = Math.round(lineHeight / 3); const frameThickness = Math.round(lineHeight / 9) * 2; + console.log(dimensions); - const computedLinesNumber = Math.ceil((headHeight + dimensions.height + arrowHeight + frameThickness) / lineHeight); + const computedLinesNumber = Math.ceil((headHeight + dimensions.height + arrowHeight + frameThickness + 8 /** margin bottom to avoid margin collapse */) / lineHeight); this._relayout(computedLinesNumber); } } 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 040faaee75b..1339e933862 100644 --- a/src/vs/workbench/parts/comments/electron-browser/media/review.css +++ b/src/vs/workbench/parts/comments/electron-browser/media/review.css @@ -34,6 +34,10 @@ display: none !important; } +.monaco-editor .review-widget .body { + overflow: hidden; +} + .monaco-editor .review-widget .body .review-comment { padding: 8px 16px 8px 20px; display: flex; @@ -198,8 +202,7 @@ } .monaco-editor .review-widget .body .comment-form { - margin: 0 20px; - padding: 8px 0; + margin: 8px 20px; } .monaco-editor .review-widget .validation-error { From 1bcf2c77ca766f23b9227ce5d18da8b10c53c803 Mon Sep 17 00:00:00 2001 From: Pine Wu Date: Thu, 31 Jan 2019 16:13:14 -0800 Subject: [PATCH 0915/1066] [CSS] Update Service --- extensions/css-language-features/server/package.json | 2 +- extensions/css-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index aebf551a2e5..6666da57282 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.10", + "vscode-css-languageservice": "^3.0.13-next.12", "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 665dd086b76..3e3e40f4a1d 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.10: - version "3.0.13-next.10" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.10.tgz#f5822e832b06e1e91ec96c528bab83bb07251c35" - integrity sha512-zKwzo3GVhrAllYDM4afL8q1XCHixsI8tP3SyLrWGzp0Nc9P+bbjKQeC26VcaOb0dtkgfpB/vfBPf+4yOs4s/pw== +vscode-css-languageservice@^3.0.13-next.12: + version "3.0.13-next.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13-next.12.tgz#8d20828e41bc7dcf44cdba4b2e476393780a7793" + integrity sha512-5B3NYU2DBFhbUvMuTg7kBlc9COHyr/pbR1cDzXGFwemQG8W6ERsgn+eftPHFbcug1kwBjPVSoMgtw/czKpboHQ== dependencies: vscode-languageserver-types "^3.13.0" vscode-nls "^4.0.0" From e43830645b83067e7c20b1375c538459589f9931 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 31 Jan 2019 16:39:15 -0800 Subject: [PATCH 0916/1066] Pick up TS 3.3.1 final Fixes #66175 --- extensions/package.json | 2 +- extensions/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/package.json b/extensions/package.json index d746f102903..0f226217b7c 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.3.0-rc" + "typescript": "3.3.1" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index d9da95bb124..faf73e87903 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.3.0-rc: - version "3.3.0-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.0-rc.tgz#700578b55868f99c79870c5611570663b3e21e44" - integrity sha512-C8FMc5pU+gFLm/rXKcwjHIeQnXJXfPNmONhKDSUWeVhyrnSqEI84EzoZylTqN0g6sJwdsn1W1AuG0RQRhfhSEA== +typescript@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== From 509e904316c406a6718aa26c1103c3705b5dcf95 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 31 Jan 2019 16:53:57 -0800 Subject: [PATCH 0917/1066] Change autofix keybinding to not use windows key Fixes #67411 --- src/vs/editor/contrib/codeAction/codeActionCommands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/codeAction/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/codeActionCommands.ts index 7eb09212d89..7777aded9f6 100644 --- a/src/vs/editor/contrib/codeAction/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/codeActionCommands.ts @@ -395,7 +395,7 @@ export class AutoFixAction extends EditorAction { contextKeyForSupportedActions(CodeActionKind.QuickFix)), kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, - primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.US_DOT, + primary: KeyMod.Alt | KeyMod.Shift | KeyCode.US_DOT, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_DOT }, From 013ce6a2ae9d9f9d8d79dc6e8146d2ce87f63794 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 31 Jan 2019 17:17:54 -0800 Subject: [PATCH 0918/1066] don't show toggle editor action when not using grid layout --- src/vs/workbench/browser/actions/layoutActions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 70bafe35ada..4ae06a85506 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -22,6 +22,7 @@ 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'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; const registry = Registry.as(Extensions.WorkbenchActions); const viewCategory = nls.localize('view', "View"); @@ -235,7 +236,7 @@ export class ToggleEditorVisibilityAction extends Action { } -registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorVisibilityAction, ToggleEditorVisibilityAction.ID, ToggleEditorVisibilityAction.LABEL), 'View: Toggle Editor Area Visibility', viewCategory, ContextKeyExpr.equals('config.workbench.useExperimentalGridLayout', true)); export class ToggleSidebarVisibilityAction extends Action { From a6ab525d5b74ea452a66057d8e60d80d24afa2fd Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Thu, 31 Jan 2019 17:27:04 -0800 Subject: [PATCH 0919/1066] refs #67018 --- src/vs/workbench/browser/parts/panel/panelPart.ts | 2 ++ src/vs/workbench/browser/parts/sidebar/sidebarPart.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 68bc0f955e6..f7e420b76a7 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -33,6 +33,7 @@ import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/cont import { isUndefinedOrNull } from 'vs/base/common/types'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; +import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; export const ActivePanelContext = new RawContextKey('activePanel', ''); export const PanelFocusContext = new RawContextKey('panelFocus', false); @@ -66,6 +67,7 @@ export class PanelPart extends CompositePart implements IPanelService, IS minimumHeight: number = 77; maximumHeight: number = Number.POSITIVE_INFINITY; snapSize: number = 50; + priority: LayoutPriority = LayoutPriority.Low; private _onDidChange = new Emitter<{ width: number; height: number; }>(); readonly onDidChange = this._onDidChange.event; diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index d95a8d716a4..88b4876943a 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -31,6 +31,7 @@ import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/cont import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; +import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview'; export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); @@ -52,6 +53,7 @@ export class SidebarPart extends CompositePart implements ISerializable minimumHeight: number = 0; maximumHeight: number = Number.POSITIVE_INFINITY; snapSize: number = 50; + priority: LayoutPriority = LayoutPriority.Low; private _onDidChange = new Emitter<{ width: number; height: number; }>(); readonly onDidChange = this._onDidChange.event; From 371c853fa9c6def40b6fa51071d0ba03c9f62ebb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 31 Jan 2019 21:35:17 -0800 Subject: [PATCH 0920/1066] Fix #67690 - only render setting trees when the settings editor is visible --- .../parts/preferences/electron-browser/settingsEditor2.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index b0d31cad7b7..dec6a197d5a 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -971,11 +971,15 @@ export class SettingsEditor2 extends BaseEditor { } private refreshTree(): void { - this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root)); + if (this.isVisible()) { + this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root)); + } } private refreshTOCTree(): void { - this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel, this.tocTree)); + if (this.isVisible()) { + this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel, this.tocTree)); + } } private updateModifiedLabelForKey(key: string): void { From 60526cb4bebf2c93db30a225a5605cb3715f283e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Feb 2019 09:29:27 +0100 Subject: [PATCH 0921/1066] rename closeTabsInMRUOrder to focusRecentEditorAfterClose (for #43459) --- src/vs/workbench/browser/parts/editor/editor.ts | 2 +- src/vs/workbench/browser/workbench.contribution.ts | 4 ++-- src/vs/workbench/common/editor.ts | 2 +- src/vs/workbench/common/editor/editorGroup.ts | 6 +++--- src/vs/workbench/test/common/editor/editorGroups.test.ts | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 09b13c06a1b..c37f2d0a275 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -30,7 +30,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { highlightModifiedTabs: false, tabCloseButton: 'right', tabSizing: 'fit', - closeTabsInMRUOrder: true, + focusRecentEditorAfterClose: true, showIcons: true, enablePreview: true, openPositioning: 'right', diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 3fcab416418..3798dbde4fd 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -58,9 +58,9 @@ configurationRegistry.registerConfiguration({ ], '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': { + 'workbench.editor.focusRecentEditorAfterClose': { 'type': 'boolean', - 'description': nls.localize('closeTabsInMRUOrder', "Controls whether tabs are closed in most recently used order or from left to right."), + 'description': nls.localize('focusRecentEditorAfterClose', "Controls whether tabs are closed in most recently used order or from left to right."), 'default': true }, 'workbench.editor.showIcons': { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index ae666377dd3..7b264e5598a 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -941,7 +941,7 @@ export interface IWorkbenchEditorPartConfiguration { highlightModifiedTabs?: boolean; tabCloseButton?: 'left' | 'right' | 'off'; tabSizing?: 'fit' | 'shrink'; - closeTabsInMRUOrder?: boolean; + focusRecentEditorAfterClose?: boolean; showIcons?: boolean; enablePreview?: boolean; enablePreviewFromQuickOpen?: boolean; diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index f8d1e48fe61..af7ff8be8bb 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -98,7 +98,7 @@ export class EditorGroup extends Disposable { private active: EditorInput | null; // editor in active state private editorOpenPositioning: 'left' | 'right' | 'first' | 'last'; - private closeTabsInMRUOrder: boolean; + private focusRecentEditorAfterClose: boolean; constructor( labelOrSerializedGroup: ISerializedEditorGroup, @@ -123,7 +123,7 @@ export class EditorGroup extends Disposable { private onConfigurationUpdated(event?: IConfigurationChangeEvent): void { this.editorOpenPositioning = this.configurationService.getValue('workbench.editor.openPositioning'); - this.closeTabsInMRUOrder = this.configurationService.getValue('workbench.editor.closeTabsInMRUOrder'); + this.focusRecentEditorAfterClose = this.configurationService.getValue('workbench.editor.focusRecentEditorAfterClose'); } get id(): GroupIdentifier { @@ -335,7 +335,7 @@ export class EditorGroup extends Disposable { // More than one editor if (this.mru.length > 1) { let newActive: EditorInput; - if (this.closeTabsInMRUOrder) { + if (this.focusRecentEditorAfterClose) { newActive = this.mru[1]; // active editor is always first in MRU, so pick second editor after as new active } else { if (index === this.editors.length - 1) { diff --git a/src/vs/workbench/test/common/editor/editorGroups.test.ts b/src/vs/workbench/test/common/editor/editorGroups.test.ts index c69335c58be..b29dbd3f74d 100644 --- a/src/vs/workbench/test/common/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/common/editor/editorGroups.test.ts @@ -29,7 +29,7 @@ function inst(): IInstantiationService { inst.stub(ITelemetryService, NullTelemetryService); const config = new TestConfigurationService(); - config.setUserConfiguration('workbench', { editor: { openPositioning: 'right', closeTabsInMRUOrder: true } }); + config.setUserConfiguration('workbench', { editor: { openPositioning: 'right', focusRecentEditorAfterClose: true } }); inst.stub(IConfigurationService, config); return inst; @@ -653,7 +653,7 @@ suite('Workbench editor groups', () => { inst.stub(ITelemetryService, NullTelemetryService); const config = new TestConfigurationService(); - config.setUserConfiguration('workbench', { editor: { closeTabsInMRUOrder: false } }); + config.setUserConfiguration('workbench', { editor: { focusRecentEditorAfterClose: false } }); inst.stub(IConfigurationService, config); const group = inst.createInstance(EditorGroup); From c2f430ea5981877e0c35925e41e2ccbd3f7b7b77 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 1 Feb 2019 09:33:19 +0100 Subject: [PATCH 0922/1066] Fix #67687 --- .../extensions/electron-browser/extensionService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index b6b74109a3a..801b69f39a2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -143,8 +143,10 @@ export class ExtensionService extends Disposable implements IExtensionService { this._extensionManagementService.onDidInstallExtension((event) => { if (event.local) { - // an extension has been installed - this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); + if (this._extensionEnablementService.isEnabled(event.local)) { + // an extension has been installed + this._handleDeltaExtensions(new DeltaExtensionsQueueItem([event.local], [])); + } } }); From cf14d2c66a3009c82e79eadf8f4a6fac409e770e Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 1 Feb 2019 11:45:27 +0100 Subject: [PATCH 0923/1066] fixes #67725 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 332fa1d0bcf..4467d58769f 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -14,7 +14,7 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; import { Iterator } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; -import { isPromiseCanceledError } from 'vs/base/common/errors'; +import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { toggleClass } from 'vs/base/browser/dom'; const enum AsyncDataTreeNodeState { @@ -675,14 +675,16 @@ export class AsyncDataTree implements IDisposable const children = await childrenPromise; this.setChildren(node, children, recursive, viewStateContext); } catch (err) { - if (isPromiseCanceledError(err)) { - return; - } + node.needsRefresh = true; if (node !== this.root) { this.tree.collapse(node === this.root ? null : node); } + if (isPromiseCanceledError(err)) { + return; + } + throw err; } finally { if (node.slow) { @@ -713,11 +715,12 @@ export class AsyncDataTree implements IDisposable } private _onDidChangeCollapseState({ node, deep }: ICollapseStateChangeEvent, any>): void { - if (!node.collapsed && node.element.state === AsyncDataTreeNodeState.Uninitialized) { + if (!node.collapsed && (node.element.state === AsyncDataTreeNodeState.Uninitialized || node.element.needsRefresh)) { if (deep) { this.collapse(node.element.element as T); } else { - this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand); + this.refreshAndRenderNode(node.element, false, ChildrenResolutionReason.Expand) + .catch(onUnexpectedError); } } } From 65cc37aba6794f065debdbc83244532996522811 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 1 Feb 2019 12:27:05 +0100 Subject: [PATCH 0924/1066] Installing new color theme causes that theme to become the active theme. Fixes #67688 --- .../electron-browser/workbenchThemeService.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index a64c0f8be21..cf132772969 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -171,17 +171,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration, tokenColorCustomizationConfiguration); if (this.currentColorTheme.isLoaded) { - if (event.added.length > 0 && event.added.length < event.themes.length) { + const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); + if (!themeData) { + // current theme is no longer available prevColorId = this.currentColorTheme.id; - this.setColorTheme(event.added[0].id, 'auto'); + this.setColorTheme(DEFAULT_THEME_ID, 'auto'); } else { - const themeData = await this.colorThemeStore.findThemeData(this.currentColorTheme.id); - if (!themeData) { - if (prevColorId && await this.colorThemeStore.findThemeData(prevColorId)) { - this.setColorTheme(prevColorId, 'auto'); - } else { - this.setColorTheme(DEFAULT_THEME_ID, 'auto'); - } + if (this.currentColorTheme.id === DEFAULT_THEME_ID && !types.isUndefined(prevColorId) && await this.colorThemeStore.findThemeData(prevColorId)) { + // restore color + this.setColorTheme(prevColorId, 'auto'); + prevColorId = undefined; } } } @@ -194,17 +193,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { configurationRegistry.notifyConfigurationSchemaUpdated(themeSettingsConfiguration); if (this.currentIconTheme.isLoaded) { - if (event.added.length > 0 && event.added.length < event.themes.length) { + const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id); + if (!theme) { + // current theme is no longer available prevFileIconId = this.currentIconTheme.id; - this.setFileIconTheme(event.added[0].id, 'auto'); + this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); } else { - const theme = await this.iconThemeStore.findThemeData(this.currentIconTheme.id); - if (!theme) { - if (prevFileIconId && await this.iconThemeStore.findThemeData(prevFileIconId)) { - this.setFileIconTheme(prevFileIconId, 'auto'); - } else { - this.setFileIconTheme(DEFAULT_ICON_THEME_SETTING_VALUE, 'auto'); - } + // restore color + if (this.currentIconTheme.id === DEFAULT_ICON_THEME_SETTING_VALUE && !types.isUndefined(prevFileIconId) && await this.iconThemeStore.findThemeData(prevFileIconId)) { + this.setFileIconTheme(prevFileIconId, 'auto'); + prevFileIconId = undefined; } } } From 3c2c17dd90bbb0200f238a4ee3542c280deb2ac5 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 1 Feb 2019 12:28:38 +0100 Subject: [PATCH 0925/1066] fixes #67708 fixes #67696 --- .../files/electron-browser/views/explorerView.ts | 14 ++++++++++++++ 1 file changed, 14 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 a972a289ee2..27befca06c6 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -233,6 +233,20 @@ export class ExplorerView extends ViewletPanel { focus(): void { this.tree.domFocus(); + + const focused = this.tree.getFocus(); + if (focused.length === 1) { + if (this.autoReveal) { + this.tree.reveal(focused[0], 0.5); + } + + const activeFile = this.getActiveFile(); + if (!activeFile && !focused[0].isDirectory) { + // Open the focused element in the editor if there is currently no file opened #67708 + this.editorService.openEditor({ resource: focused[0].resource, options: { preserveFocus: true, revealIfVisible: true } }) + .then(undefined, onUnexpectedError); + } + } } private selectActiveFile(reveal?: boolean): void { From 98e5b5c0a30591571b5a39aa921cc3f27cea27c9 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 1 Feb 2019 12:47:55 +0100 Subject: [PATCH 0926/1066] fixes #67720 --- src/vs/base/browser/ui/tree/abstractTree.ts | 36 +++++++++++++++---- src/vs/base/browser/ui/tree/indexTreeModel.ts | 2 +- src/vs/base/browser/ui/tree/tree.ts | 1 + 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index c68823146a3..c5640d99420 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -870,12 +870,36 @@ export abstract class AbstractTree implements IDisposable this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; - this.model.onDidSplice(e => { - this.eventBufferer.bufferEvents(() => { - this.focus.remove(e.deletedNodes); - this.selection.remove(e.deletedNodes); - }); - }, null, this.disposables); + if (this.options.identityProvider) { + const identityProvider = this.options.identityProvider; + + this.model.onDidSplice(e => { + if (e.deletedNodes.length === 0) { + return; + } + + this.eventBufferer.bufferEvents(() => { + const map = new Map>(); + + for (const node of e.deletedNodes) { + map.set(identityProvider.getId(node.element).toString(), node); + } + + for (const node of e.insertedNodes) { + map.delete(identityProvider.getId(node.element).toString()); + } + + if (map.size === 0) { + return; + } + + const deletedNodes = values(map); + + this.focus.remove(deletedNodes); + this.selection.remove(deletedNodes); + }); + }, null, this.disposables); + } this.view.onTap(this.reactOnMouseClick, this, this.disposables); this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index b22af463d96..dca017dbfb0 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -173,7 +173,7 @@ export class IndexTreeModel, TFilterData = voi } const result = Iterator.map(Iterator.fromArray(deletedNodes), treeNodeToElement); - this._onDidSplice.fire({ deletedNodes }); + this._onDidSplice.fire({ insertedNodes: nodesToInsert, deletedNodes }); return result; } diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index adde3a3f6f2..1cdd610004a 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -98,6 +98,7 @@ export interface ICollapseStateChangeEvent { } export interface ITreeModelSpliceEvent { + insertedNodes: ITreeNode[]; deletedNodes: ITreeNode[]; } From e6c682e841cf7ea813ac70f036e9ad0471640bef Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 1 Feb 2019 13:57:23 +0100 Subject: [PATCH 0927/1066] refresh the proper item (parent) when item resolved fixes #67733 --- .../parts/files/electron-browser/explorerService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/files/electron-browser/explorerService.ts b/src/vs/workbench/parts/files/electron-browser/explorerService.ts index e6edda05b16..7bcd9095155 100644 --- a/src/vs/workbench/parts/files/electron-browser/explorerService.ts +++ b/src/vs/workbench/parts/files/electron-browser/explorerService.ts @@ -151,10 +151,11 @@ export class ExplorerService implements IExplorerService { const modelStat = ExplorerItem.create(stat, undefined, options.resolveTo); // Update Input with disk Stat ExplorerItem.mergeLocalWithDisk(modelStat, root); - this._onDidChangeItem.fire(root); + const item = root.find(resource); + this._onDidChangeItem.fire(item ? item.parent : undefined); // Select and Reveal - this._onDidSelectItem.fire({ item: root.find(resource) || undefined, reveal }); + this._onDidSelectItem.fire({ item: item || undefined, reveal }); }, () => { root.isError = true; this._onDidChangeItem.fire(root); From 6b552b4f4dfcb9bf60158d3024f97656a48a24d8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Feb 2019 14:12:57 +0100 Subject: [PATCH 0928/1066] files - let copy/move of same file path be a no-op --- .../files/electron-browser/fileService.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index a08ba498f27..f6419c6094f 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -856,25 +856,28 @@ export class FileService extends Disposable implements IFileService { // 2.) resolve return this.resolve(target).then(result => { - // Events - this._onAfterOperation.fire(new FileOperationEvent(source, keepCopy ? FileOperation.COPY : FileOperation.MOVE, result)); + // Events (unless it was a no-op because paths are identical) + if (sourcePath !== targetPath) { + this._onAfterOperation.fire(new FileOperationEvent(source, keepCopy ? FileOperation.COPY : FileOperation.MOVE, result)); + } return result; }); }); } - private doMoveOrCopyFile(sourcePath: string, targetPath: string, keepCopy: boolean, overwrite: boolean): Promise { + private doMoveOrCopyFile(sourcePath: string, targetPath: string, keepCopy: boolean, overwrite: boolean): Promise { // 1.) validate operation if (isParent(targetPath, sourcePath, !isLinux)) { return Promise.reject(new Error('Unable to move/copy when source path is parent of target path')); + } else if (sourcePath === targetPath) { + return Promise.resolve(); // no-op but not an error } // 2.) check if target exists return pfs.exists(targetPath).then(exists => { const isCaseRename = sourcePath.toLowerCase() === targetPath.toLowerCase(); - const isSameFile = sourcePath === targetPath; // Return early with conflict if target exists and we are not told to overwrite if (exists && !isCaseRename && !overwrite) { @@ -897,14 +900,12 @@ export class FileService extends Disposable implements IFileService { return pfs.mkdirp(paths.dirname(targetPath)).then(() => { // 4.) copy/move - if (isSameFile) { - return null; - } else if (keepCopy) { + if (keepCopy) { return nfcall(extfs.copy, sourcePath, targetPath); } else { return nfcall(extfs.mv, sourcePath, targetPath); } - }).then(() => exists); + }); }); }); } From bece8283521c1099ea7be5013a99418cdb799ef5 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Fri, 1 Feb 2019 14:21:04 +0100 Subject: [PATCH 0929/1066] Disable a theme does not exclude it from `workbench.colorTheme` completion. Fixes #67465 --- .../themes/electron-browser/workbenchThemeService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index cf132772969..e0d5b16bf91 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -149,16 +149,16 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { // update settings schema setting for theme specific settings this.colorThemeStore.onDidChange(async event => { + // updates enum for the 'workbench.colorTheme` setting + colorThemeSettingSchema.enum = event.themes.map(t => t.settingsId); + colorThemeSettingSchema.enumDescriptions = event.themes.map(t => t.description || ''); + const themeSpecificWorkbenchColors: IJSONSchema = { properties: {} }; const themeSpecificTokenColors: IJSONSchema = { properties: {} }; const workbenchColors = { $ref: workbenchColorsSchemaId, additionalProperties: false }; const tokenColors = { properties: tokenColorSchema.properties, additionalProperties: false }; for (let t of event.themes) { - // add a enum value to the 'workbench.colorTheme` setting - colorThemeSettingSchema.enum!.push(t.settingsId); - colorThemeSettingSchema.enumDescriptions!.push(t.description || ''); - // add theme specific color customization ("[Abyss]":{ ... }) const themeId = `[${t.settingsId}]`; themeSpecificWorkbenchColors.properties![themeId] = workbenchColors; From 1d2b3be2feb1700ba94d46de9c3024015a62de41 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 1 Feb 2019 14:21:48 +0100 Subject: [PATCH 0930/1066] move openOnSingleClick to tree fixes #65252 --- src/vs/base/browser/ui/tree/abstractTree.ts | 6 ++++ src/vs/base/browser/ui/tree/asyncDataTree.ts | 1 + src/vs/platform/list/browser/listService.ts | 36 +++++++------------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index c5640d99420..ce8d7dc4e71 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -672,6 +672,7 @@ export interface IAbstractTreeOptionsUpdate extends ITreeRendererOptions { readonly automaticKeyboardNavigation?: boolean; readonly simpleKeyboardNavigation?: boolean; readonly filterOnType?: boolean; + readonly openOnSingleClick?: boolean; } export interface IAbstractTreeOptions extends IAbstractTreeOptionsUpdate, IListOptions { @@ -842,6 +843,7 @@ export abstract class AbstractTree implements IDisposable readonly onWillRefilter: Event = this._onWillRefilter.event; get filterOnType(): boolean { return !!this._options.filterOnType; } + get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } get onDidDispose(): Event { return this.view.onDidDispose; } @@ -1156,6 +1158,10 @@ export abstract class AbstractTree implements IDisposable return; } + if (!this.openOnSingleClick && e.browserEvent.detail !== 2) { + return; + } + const location = this.model.getNodeLocation(node); const recursive = e.browserEvent.altKey; diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 4467d58769f..bfe2b0e6161 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -287,6 +287,7 @@ export class AsyncDataTree implements IDisposable get onDidBlur(): Event { return this.tree.onDidBlur; } get filterOnType(): boolean { return this.tree.filterOnType; } + get openOnSingleClick(): boolean { return this.tree.openOnSingleClick; } get onDidDispose(): Event { return this.tree.onDidDispose; } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index e3ac00702cd..9a6688e2d15 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -909,7 +909,6 @@ export class WorkbenchObjectTree, TFilterData = void> private hasDoubleSelection: IContextKey; private hasMultiSelection: IContextKey; - private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -929,6 +928,7 @@ export class WorkbenchObjectTree, TFilterData = void> const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + const openOnSingleClick = useSingleClickToOpen(configurationService); super(container, delegate, renderers, { keyboardSupport: false, @@ -939,7 +939,8 @@ export class WorkbenchObjectTree, TFilterData = void> automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', - horizontalScrolling + horizontalScrolling, + openOnSingleClick }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -951,7 +952,6 @@ export class WorkbenchObjectTree, TFilterData = void> this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); - this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); const interestingContextKeys = new Set(); @@ -977,7 +977,7 @@ export class WorkbenchObjectTree, TFilterData = void> }), configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(openModeSettingKey)) { - this._openOnSingleClick = useSingleClickToOpen(configurationService); + this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); @@ -1005,10 +1005,6 @@ export class WorkbenchObjectTree, TFilterData = void> ); } - get openOnSingleClick(): boolean { - return this._openOnSingleClick; - } - get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } @@ -1027,7 +1023,6 @@ export class WorkbenchDataTree extends DataTree; private hasMultiSelection: IContextKey; - private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -1048,6 +1043,7 @@ export class WorkbenchDataTree extends DataTree(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + const openOnSingleClick = useSingleClickToOpen(configurationService); super(container, delegate, renderers, dataSource, { keyboardSupport: false, @@ -1058,7 +1054,8 @@ export class WorkbenchDataTree extends DataTree extends DataTree extends DataTree { if (e.affectsConfiguration(openModeSettingKey)) { - this._openOnSingleClick = useSingleClickToOpen(configurationService); + this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); @@ -1124,10 +1120,6 @@ export class WorkbenchDataTree extends DataTree extends Async private hasDoubleSelection: IContextKey; private hasMultiSelection: IContextKey; - private _openOnSingleClick: boolean; private _useAltAsMultipleSelectionModifier: boolean; constructor( @@ -1162,6 +1153,7 @@ export class WorkbenchAsyncDataTree extends Async const automaticKeyboardNavigation = contextKeyService.getContextKeyValue(WorkbenchListAutomaticKeyboardNavigationKey); const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + const openOnSingleClick = useSingleClickToOpen(configurationService); super(container, delegate, renderers, dataSource, { keyboardSupport: false, @@ -1172,7 +1164,8 @@ export class WorkbenchAsyncDataTree extends Async automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', - horizontalScrolling + horizontalScrolling, + openOnSingleClick }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -1184,7 +1177,6 @@ export class WorkbenchAsyncDataTree extends Async this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService); this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService); - this._openOnSingleClick = useSingleClickToOpen(configurationService); this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); const interestingContextKeys = new Set(); @@ -1210,7 +1202,7 @@ export class WorkbenchAsyncDataTree extends Async }), configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(openModeSettingKey)) { - this._openOnSingleClick = useSingleClickToOpen(configurationService); + this.updateOptions({ openOnSingleClick: useSingleClickToOpen(configurationService) }); } if (e.affectsConfiguration(multiSelectModifierSettingKey)) { this._useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); @@ -1238,10 +1230,6 @@ export class WorkbenchAsyncDataTree extends Async ); } - get openOnSingleClick(): boolean { - return this._openOnSingleClick; - } - get useAltAsMultipleSelectionModifier(): boolean { return this._useAltAsMultipleSelectionModifier; } From 201d02554cd997c57b1e73821e041dbc700b308b Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 1 Feb 2019 14:38:51 +0100 Subject: [PATCH 0931/1066] fixes #67716 --- .../parts/files/electron-browser/views/explorerViewer.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 fe5b0957b70..e66f74f034a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts @@ -445,6 +445,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const isCopy = originalEvent && ((originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh)); const fromDesktop = data instanceof DesktopDragAndDropData; + const effect = (fromDesktop || isCopy) ? ListDragOverEffect.Copy : ListDragOverEffect.Move; // Desktop DND if (fromDesktop) { @@ -474,7 +475,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { return false; } - return { accept: true, bubble: TreeDragOverBubble.Down, autoExpand: false }; + return { accept: true, bubble: TreeDragOverBubble.Down, effect, autoExpand: false }; } if (!Array.isArray(items)) { @@ -511,13 +512,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // All (target = model) if (!target) { - return { accept: true, bubble: TreeDragOverBubble.Down }; + return { accept: true, bubble: TreeDragOverBubble.Down, effect }; } // All (target = file/folder) else { - const effect = fromDesktop || isCopy ? ListDragOverEffect.Copy : ListDragOverEffect.Move; - if (target.isDirectory) { if (target.isReadonly) { return false; From f125fdfa20b217dea34001eac9866322418226ce Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 1 Feb 2019 15:06:15 +0100 Subject: [PATCH 0932/1066] fixes #67726 --- src/vs/base/browser/ui/tree/abstractTree.ts | 9 ++++++ src/vs/platform/list/browser/listService.ts | 31 +++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index ce8d7dc4e71..742c6aa0d8e 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -392,6 +392,7 @@ class TypeFilterController implements IDisposable { private labelDomNode: HTMLElement; private filterOnTypeDomNode: HTMLInputElement; private clearDomNode: HTMLElement; + private keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; private automaticKeyboardNavigation: boolean; private triggered = false; @@ -426,6 +427,8 @@ class TypeFilterController implements IDisposable { this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); + this.keyboardNavigationEventFilter = tree.options.keyboardNavigationEventFilter; + model.onDidSplice(this.onDidSpliceModel, this, this.disposables); this.updateOptions(tree.options); } @@ -463,6 +466,7 @@ class TypeFilterController implements IDisposable { const isPrintableCharEvent = this.keyboardNavigationLabelProvider.mightProducePrintableCharacter ? (e: IKeyboardEvent) => this.keyboardNavigationLabelProvider.mightProducePrintableCharacter!(e) : (e: IKeyboardEvent) => mightProducePrintableCharacter(e); const onKeyDown = Event.chain(domEvent(this.view.getHTMLElement(), 'keydown')) .filter(e => !isInputElement(e.target as HTMLElement) || e.target === this.filterOnTypeDomNode) + .filter(this.keyboardNavigationEventFilter || (() => true)) .filter(() => this.automaticKeyboardNavigation || this.triggered) .map(e => new StandardKeyboardEvent(e)) .filter(e => isPrintableCharEvent(e) || ((this._pattern.length > 0 || this.triggered) && ((e.keyCode === KeyCode.Escape || e.keyCode === KeyCode.Backspace) && !e.altKey && !e.ctrlKey && !e.metaKey) || (e.keyCode === KeyCode.Backspace && (isMacintosh ? e.altKey : e.ctrlKey)))) @@ -668,6 +672,10 @@ function asTreeContextMenuEvent(event: IListContextMenuEvent extends IAbstractTr readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; readonly autoExpandSingleChildren?: boolean; + readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; } /** diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 9a6688e2d15..3b7b94de788 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -35,6 +35,7 @@ import { ObjectTree, IObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTr import { ITreeEvent, ITreeRenderer, IAsyncDataSource, IDataSource } from 'vs/base/browser/ui/tree/tree'; import { AsyncDataTree, IAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree'; import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree'; +import { IKeyboardNavigationEventFilter } from 'vs/base/browser/ui/tree/abstractTree'; export type ListWidget = List | PagedList | ITree | ObjectTree | DataTree | AsyncDataTree; @@ -899,6 +900,27 @@ export class HighlightingWorkbenchTree extends WorkbenchTree { } } +function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingService: IKeybindingService): IKeyboardNavigationEventFilter { + let inChord = false; + + return event => { + if (inChord) { + inChord = false; + return false; + } + + const result = keybindingService.softDispatch(event, container); + + if (result && result.enterChord) { + inChord = true; + return false; + } + + inChord = false; + return true; + }; +} + export class WorkbenchObjectTree, TFilterData = void> extends ObjectTree { readonly contextKeyService: IContextKeyService; @@ -940,7 +962,8 @@ export class WorkbenchObjectTree, TFilterData = void> simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', horizontalScrolling, - openOnSingleClick + openOnSingleClick, + keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); @@ -1055,7 +1078,8 @@ export class WorkbenchDataTree extends DataTree extends Async simpleKeyboardNavigation: keyboardNavigation === 'simple', filterOnType: keyboardNavigation === 'filter', horizontalScrolling, - openOnSingleClick + openOnSingleClick, + keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) }); this.contextKeyService = createScopedContextKeyService(contextKeyService, this); From 7a24ac1de4724b8e9d564814f2e654b02eab04b6 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 1 Feb 2019 15:51:13 +0100 Subject: [PATCH 0933/1066] explorer: do not select and reveal roots which are tree inputs fixes #67749 --- .../parts/files/electron-browser/views/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 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 27befca06c6..247910d03e8 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -503,7 +503,7 @@ export class ExplorerView extends ViewletPanel { } private onSelectItem(fileStat: ExplorerItem, reveal = this.autoReveal): Promise { - if (!fileStat || !this.isBodyVisible()) { + if (!fileStat || !this.isBodyVisible() || this.tree.getInput() === fileStat) { return Promise.resolve(undefined); } From 5b966deaf193ab7d443314ac31a93f4557849102 Mon Sep 17 00:00:00 2001 From: isidor Date: Fri, 1 Feb 2019 16:06:25 +0100 Subject: [PATCH 0934/1066] explorer issues to bpasero --- .github/classifier.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/classifier.yml b/.github/classifier.yml index 3236507a472..a279a7cb8d2 100644 --- a/.github/classifier.yml +++ b/.github/classifier.yml @@ -63,7 +63,7 @@ assignLabel: false }, file-explorer: { - assignees: [ isidorn ], + assignees: [ bpasero ], assignLabel: false }, file-glob: [], From 8020bdcf22a99407f26dfbecf820f7ddb4073784 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Fri, 1 Feb 2019 16:52:33 +0100 Subject: [PATCH 0935/1066] fixes #67755 --- src/vs/base/browser/ui/list/listWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 37ccdcbf94f..53869580549 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1066,7 +1066,7 @@ export class List implements ISpliceable, IDisposable { private mouseController: MouseController | undefined; get multipleSelectionController(): IMultipleSelectionController { - return this.mouseController ? this.mouseController.multipleSelectionController : DefaultMultipleSelectionContoller; + return (this.mouseController && this.mouseController.multipleSelectionController) || DefaultMultipleSelectionContoller; } private _onDidUpdateOptions = new Emitter>(); From 7523941bc9decc58504f9dc710961eb020f66fa9 Mon Sep 17 00:00:00 2001 From: Miguel Solorio Date: Fri, 1 Feb 2019 08:43:43 -0800 Subject: [PATCH 0936/1066] Increase spacing between keybinding keys --- .../browser/ui/keybindingLabel/keybindingLabel.css | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css index dd24df5df23..e33663d34b1 100644 --- a/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css +++ b/src/vs/base/browser/ui/keybindingLabel/keybindingLabel.css @@ -20,6 +20,15 @@ color: #555; font-size: 11px; padding: 3px 5px; + margin: 0 2px; +} + +.monaco-keybinding > .monaco-keybinding-key:first-child { + margin-left: 0; +} + +.monaco-keybinding > .monaco-keybinding-key:last-child { + margin-right: 0; } .hc-black .monaco-keybinding > .monaco-keybinding-key, @@ -36,5 +45,5 @@ } .monaco-keybinding > .monaco-keybinding-key-chord-separator { - width: 2px; + width: 6px; } \ No newline at end of file From 8f56013e8f66650c1b0b65cc5c502691e307db21 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 1 Feb 2019 10:05:32 -0800 Subject: [PATCH 0937/1066] Fix #67757 --- .../electron-browser/settingsEditor2.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index dec6a197d5a..3d677bad6fd 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -549,24 +549,25 @@ export class SettingsEditor2 extends BaseEditor { this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { - setTimeout(() => { - const element: SettingsTreeGroupElement = e.elements[0]; - if (this.tocFocusedElement === e.elements[0]) { - return; - } + const element: SettingsTreeGroupElement = e.elements[0]; + if (this.tocFocusedElement === element) { + return; + } - this.tocFocusedElement = element; - this.tocTree.setSelection(e.elements); - if (this.searchResultModel) { - if (this.viewState.filterToCategory !== element) { - this.viewState.filterToCategory = element; + this.tocFocusedElement = element; + this.tocTree.setSelection(element ? [element] : []); + if (this.searchResultModel) { + if (this.viewState.filterToCategory !== element) { + this.viewState.filterToCategory = element; + // see https://github.com/Microsoft/vscode/issues/66796 + setTimeout(() => { this.renderTree(); this.settingsTree.scrollTop = 0; - } - } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { - this.settingsTree.reveal(element, 0); + }, 0); } - }, 0); + } else if (element && (!e.browserEvent || !(e.browserEvent).fromScroll)) { + this.settingsTree.reveal(element, 0); + } })); this._register(this.tocTree.onDidFocus(() => { From 8eb58eba797971ba4cdd65d19b032212526bcb3e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 1 Feb 2019 10:34:57 -0800 Subject: [PATCH 0938/1066] Bump to 1.32 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 781fc7bde22..75e5b7f1d38 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.31.0", + "version": "1.32.0", "distro": "cb7599e6323871114d034681abb30ed5e2066d3c", "author": { "name": "Microsoft Corporation" From c3b9299c9d68bb566d946e8f7260177471be8de3 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Fri, 1 Feb 2019 11:53:04 -0800 Subject: [PATCH 0939/1066] disable save actions for webview fixes #60801 --- .../files/electron-browser/fileActions.contribution.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 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 1002950c754..ca9a038f774 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -25,6 +25,8 @@ import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listServi import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor'; +import { WalkThroughPart } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; // Contribute Global Actions const category = nls.localize('filesCategory', "File"); @@ -559,7 +561,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '4_save', command: { id: SAVE_FILE_COMMAND_ID, - title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save") + title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), + precondition: ContextKeyExpr.and(ContextKeyExpr.notEquals('activeEditor', WebviewEditor.ID), ContextKeyExpr.notEquals('activeEditor', WalkThroughPart.ID)) }, order: 1 }); @@ -568,7 +571,8 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '4_save', command: { id: SAVE_FILE_AS_COMMAND_ID, - title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") + title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), + precondition: ContextKeyExpr.and(ContextKeyExpr.notEquals('activeEditor', WebviewEditor.ID), ContextKeyExpr.notEquals('activeEditor', WalkThroughPart.ID)) }, order: 2 }); From c79a8bf7eb56804c25e943533640b2a3f76ff0d3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 14:45:26 -0800 Subject: [PATCH 0940/1066] Remove stray log :) --- extensions/markdown-language-features/src/markdownEngine.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 90e7284cf2b..25ac8f33b09 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -283,7 +283,6 @@ async function getMarkdownOptions(md: () => MarkdownIt) { return { html: true, highlight: (str: string, lang?: string) => { - console.log(123); // Workaround for highlight not supporting tsx: https://github.com/isagalaev/highlight.js/issues/1155 if (lang && ['tsx', 'typescriptreact'].indexOf(lang.toLocaleLowerCase()) >= 0) { lang = 'jsx'; From ea541de9c470f1f31d5d8b8034587d8b72c4daf9 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 14:46:33 -0800 Subject: [PATCH 0941/1066] Remove old css vars from webviews These were undocumented and we now had offical css vars that extensions should use instead --- .../parts/webview/electron-browser/webviewElement.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index 5d9110300fb..d65d118c5bb 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -475,16 +475,6 @@ export class WebviewElement extends Disposable { const styles = { - // Old vars - 'font-family': fontFamily, - 'font-weight': fontWeight, - 'font-size': fontSize, - 'background-color': theme.getColor(colorRegistry.editorBackground).toString(), - 'color': theme.getColor(colorRegistry.editorForeground).toString(), - 'link-color': theme.getColor(colorRegistry.textLinkForeground).toString(), - 'link-active-color': theme.getColor(colorRegistry.textLinkActiveForeground).toString(), - - // Offical API 'vscode-editor-font-family': fontFamily, 'vscode-editor-font-weight': fontWeight, 'vscode-editor-font-size': fontSize, @@ -495,6 +485,7 @@ export class WebviewElement extends Disposable { this._send('styles', styles, activeTheme); this._webviewFindWidget.updateTheme(theme); + } public layout(): void { From 543282c05b14f9b558ba55b80c393149140ca96a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 15:07:34 -0800 Subject: [PATCH 0942/1066] Make using the previewHtml command a user facing error #62630 --- src/vs/workbench/api/node/extHost.api.impl.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 02fd3f33913..59ecf348f91 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -177,11 +177,11 @@ export function createApiFactory( // Warn when trying to use the vscode.previewHtml command as it does not work properly in all scenarios and // has security concerns. const checkCommand = (() => { - let done = !extension.isUnderDevelopment; + let done = false; const informOnce = () => { if (!done) { done = true; - console.warn(`Extension '${extension.identifier.value}' uses the 'vscode.previewHtml' command which is deprecated and will be removed. Please update your extension to use the Webview API: https://go.microsoft.com/fwlink/?linkid=2039309`); + window.showWarningMessage(localize('previewHtml.deprecated', "Extension '{0}' uses the 'vscode.previewHtml' command which is deprecated and will be removed soon. Please file an issue against this extension to update to use VS Code's webview API.", extension.identifier.value)); } }; return (commandId: string) => { From 1d90e2f39b83664a208e244ddaf9d8125eea2c15 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 15:12:27 -0800 Subject: [PATCH 0943/1066] Strict null auto add --- src/tsconfig.strictNullChecks.json | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 530d76501d5..19d2a4737a0 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -18,7 +18,7 @@ "./vs/editor/contrib/smartSelect/**/*.ts", "./vs/editor/contrib/snippet/**/*.ts", "./vs/editor/contrib/suggest/**/*.ts", - "./vs/editor/test/**/*.ts", + "./vs/editor/test/**/*.ts" ], "files": [ "./vs/base/parts/contextmenu/common/contextmenu.ts", @@ -171,9 +171,9 @@ "./vs/editor/contrib/comment/test/lineCommentCommand.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/documentSymbols/outlineModel.ts", "./vs/editor/contrib/find/findController.ts", "./vs/editor/contrib/find/findDecorations.ts", "./vs/editor/contrib/find/findModel.ts", @@ -361,6 +361,7 @@ "./vs/platform/editor/common/editor.ts", "./vs/platform/environment/common/environment.ts", "./vs/platform/environment/node/argv.ts", + "./vs/platform/environment/node/argvHelper.ts", "./vs/platform/environment/node/environmentService.ts", "./vs/platform/environment/test/node/environmentService.test.ts", "./vs/platform/extensionManagement/common/extensionEnablementService.ts", @@ -378,6 +379,7 @@ "./vs/platform/extensions/common/extensionHost.ts", "./vs/platform/extensions/common/extensions.ts", "./vs/platform/extensions/node/extensionValidator.ts", + "./vs/platform/extensions/node/extensionsUtil.ts", "./vs/platform/extensions/test/node/extensionValidator.test.ts", "./vs/platform/files/common/files.ts", "./vs/platform/files/node/files.ts", @@ -508,6 +510,8 @@ "./vs/platform/workspaces/node/workspacesIpc.ts", "./vs/vscode.d.ts", "./vs/vscode.proposed.d.ts", + "./vs/workbench/api/common/configurationExtensionPoint.ts", + "./vs/workbench/api/common/menusExtensionPoint.ts", "./vs/workbench/api/node/extHostExtensionActivator.ts", "./vs/workbench/api/node/extHostSearch.fileIndex.ts", "./vs/workbench/api/node/extHostTypes.ts", @@ -554,6 +558,7 @@ "./vs/workbench/browser/parts/views/views.ts", "./vs/workbench/browser/parts/views/viewsViewlet.ts", "./vs/workbench/browser/viewlet.ts", + "./vs/workbench/browser/workbench.contribution.ts", "./vs/workbench/common/actions.ts", "./vs/workbench/common/activity.ts", "./vs/workbench/common/component.ts", @@ -572,9 +577,12 @@ "./vs/workbench/common/theme.ts", "./vs/workbench/common/viewlet.ts", "./vs/workbench/common/views.ts", - "./vs/workbench/electron-browser/actions/helpActions", "./vs/workbench/electron-browser/actions/developerActions", + "./vs/workbench/electron-browser/actions/developerActions.ts", + "./vs/workbench/electron-browser/actions/helpActions", + "./vs/workbench/electron-browser/actions/helpActions.ts", "./vs/workbench/electron-browser/actions/windowActions", + "./vs/workbench/electron-browser/actions/windowActions.ts", "./vs/workbench/electron-browser/resources.ts", "./vs/workbench/electron-browser/window.ts", "./vs/workbench/parts/backup/common/backupRestorer.ts", @@ -747,8 +755,6 @@ "./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/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", @@ -869,8 +875,8 @@ "./vs/workbench/services/themes/electron-browser/colorThemeStore.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeData.ts", "./vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts", - "./vs/workbench/services/themes/electron-browser/workbenchThemeService.ts", "./vs/workbench/services/themes/electron-browser/themeCompatibility.ts", + "./vs/workbench/services/themes/electron-browser/workbenchThemeService.ts", "./vs/workbench/services/timer/electron-browser/timerService.ts", "./vs/workbench/services/title/common/titleService.ts", "./vs/workbench/services/viewlet/browser/viewlet.ts", From 9f6f45bb0917385798e42754c10f825c802aa3c2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 15:31:19 -0800 Subject: [PATCH 0944/1066] Build VS Code using TS 3.3 Fixes #67776 --- build/package.json | 4 ++-- build/yarn.lock | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/build/package.json b/build/package.json index 64c0f9277b0..4ad039a7e21 100644 --- a/build/package.json +++ b/build/package.json @@ -38,7 +38,7 @@ "minimist": "^1.2.0", "request": "^2.85.0", "tslint": "^5.9.1", - "typescript": "3.2.2", + "typescript": "3.3.1", "vsce": "1.48.0", "xml2js": "^0.4.17" }, @@ -48,4 +48,4 @@ "postinstall": "npm run compile", "npmCheckJs": "tsc --noEmit" } -} +} \ No newline at end of file diff --git a/build/yarn.lock b/build/yarn.lock index 4d2a53c8ee7..c9384fe6d54 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -1894,10 +1894,10 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" - integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== +typescript@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" diff --git a/package.json b/package.json index 75e5b7f1d38..0d10f5eb1f5 100644 --- a/package.json +++ b/package.json @@ -125,7 +125,7 @@ "source-map": "^0.4.4", "ts-loader": "^4.4.2", "tslint": "^5.11.0", - "typescript": "3.2.2", + "typescript": "3.3.1", "typescript-formatter": "7.1.0", "typescript-tslint-plugin": "^0.0.7", "uglify-es": "^3.0.18", diff --git a/yarn.lock b/yarn.lock index 42352c9383a..bf5199ef494 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8828,10 +8828,10 @@ typescript-tslint-plugin@^0.0.7: minimatch "^3.0.4" vscode-languageserver "^5.1.0" -typescript@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" - integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== +typescript@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.1.tgz#6de14e1db4b8a006ac535e482c8ba018c55f750b" + integrity sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA== typescript@^2.6.2: version "2.6.2" From b4165f6d32a4ec473a489b97e059b262d1da9dfb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Fri, 1 Feb 2019 15:46:03 -0800 Subject: [PATCH 0945/1066] Fix potential race while creating a webview and changing a theme Fixes #67628 --- .../webview/electron-browser/webview-pre.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js index 29593dc6b66..ebf0555f42f 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js @@ -36,7 +36,7 @@ * @param {() => void} handlers.onFocus * @param {() => void} handlers.onBlur */ - const trackFocus = ({onFocus, onBlur}) => { + const trackFocus = ({ onFocus, onBlur }) => { const interval = 50; let isFocused = document.hasFocus(); setInterval(() => { @@ -67,12 +67,19 @@ /** * @param {HTMLElement} body */ - const styleBody = (body) => { + const applyStyles = (body) => { if (!body) { return; } + body.classList.remove('vscode-light', 'vscode-dark', 'vscode-high-contrast'); body.classList.add(initData.activeTheme); + + if (initData.styles) { + for (const variable of Object.keys(initData.styles)) { + body.style.setProperty(`--${variable}`, initData.styles[variable]); + } + } }; const getActiveFrame = () => { @@ -169,11 +176,7 @@ return; } - styleBody(target.contentDocument.body); - - Object.keys(variables).forEach((variable) => { - target.contentDocument.documentElement.style.setProperty(`--${variable}`, variables[variable]); - }); + applyStyles(target.contentDocument.body); }); // propagate focus @@ -253,7 +256,7 @@ defaultStyles.innerHTML = getDefaultCss(initData.styles); newDocument.head.prepend(defaultStyles); - styleBody(newDocument.body); + applyStyles(newDocument.body); const frame = getActiveFrame(); const wasFirstLoad = firstLoad; @@ -321,6 +324,8 @@ if (oldActiveFrame) { document.body.removeChild(oldActiveFrame); } + // Styles may have changed since we created the element. Make sure we re-style + applyStyles(newFrame.contentDocument.body); newFrame.setAttribute('id', 'active-frame'); newFrame.style.visibility = 'visible'; newFrame.contentWindow.focus(); From 7ed75d992c5999730ce97d03caa482f0b7da596d Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 1 Feb 2019 19:57:12 -0800 Subject: [PATCH 0946/1066] vscode-xterm@3.12.0-beta1 Fixes #67438 --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0d10f5eb1f5..561f2d5c494 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.11.0-beta2", + "vscode-xterm": "3.12.0-beta1", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" @@ -150,4 +150,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index bf5199ef494..bfb1e38ac88 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.11.0-beta2: - version "3.11.0-beta2" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.11.0-beta2.tgz#fe5295c835fcff00fee55cbb1d5a4bf537d105a2" - integrity sha512-QG9ALgfH4+YUblN1G/AAdf40MJPOeb0p2u2k9MwcIvaJGQmk5Pp2KZUiQbO+ycfeuisTTVtqjceG7MutIEVZSQ== +vscode-xterm@3.12.0-beta1: + version "3.12.0-beta1" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.12.0-beta1.tgz#7dc28214077d1d8fc306fb6f392b4b553c6b0024" + integrity sha512-bv6AbbtzMfNdku97BomTY7PJNo1Bd0sHnNy9GZmMeVm8SoLxxvhmR2PRsbYY0AD1n7wbA7l55msJKye57+mD/w== vso-node-api@6.1.2-preview: version "6.1.2-preview" From 2b0fc3b0fcf78cd3d0bed3619c2b6f58fcf0411c Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Sat, 2 Feb 2019 09:01:49 -0800 Subject: [PATCH 0947/1066] Fix infinite relayout of comment widget. --- .../parts/comments/electron-browser/commentThreadWidget.ts | 1 - .../parts/comments/electron-browser/simpleCommentEditor.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts index 456f7b9be20..c5eea6a8d01 100644 --- a/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts +++ b/src/vs/workbench/parts/comments/electron-browser/commentThreadWidget.ts @@ -622,7 +622,6 @@ export class ReviewZoneWidget extends ZoneWidget { const lineHeight = this.editor.getConfiguration().lineHeight; const arrowHeight = Math.round(lineHeight / 3); const frameThickness = Math.round(lineHeight / 9) * 2; - console.log(dimensions); const computedLinesNumber = Math.ceil((headHeight + dimensions.height + arrowHeight + frameThickness + 8 /** margin bottom to avoid margin collapse */) / lineHeight); this._relayout(computedLinesNumber); diff --git a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts index 81a65c067a2..779d0e58fe7 100644 --- a/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts +++ b/src/vs/workbench/parts/comments/electron-browser/simpleCommentEditor.ts @@ -63,7 +63,6 @@ export class SimpleCommentEditor extends CodeEditorWidget { verticalHasArrows: false, horizontalHasArrows: false }, - automaticLayout: true, overviewRulerLanes: 2, lineDecorationsWidth: 0, scrollBeyondLastLine: false, From d6b0453f41a5ccf1cef25bc901eda07368874696 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 2 Feb 2019 10:38:51 -0800 Subject: [PATCH 0948/1066] Fix conflict --- .../parts/terminal/electron-browser/terminalActions.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 7ed5a34cc68..506ff36c559 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -30,13 +30,10 @@ 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'; -<<<<<<< HEAD import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { Schemas } from 'vs/base/common/network'; -======= import { URI } from 'vs/base/common/uri'; ->>>>>>> origin/master export const TERMINAL_PICKER_PREFIX = 'term '; From ac416127cafe37c1a4f348c7c832541e5a912971 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Sat, 2 Feb 2019 10:44:53 -0800 Subject: [PATCH 0949/1066] Remove empty lines --- .../parts/terminal/electron-browser/terminalActions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts index 506ff36c559..4252f8fa3df 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalActions.ts @@ -288,12 +288,10 @@ export class SendSequenceTerminalCommand extends Command { } const configurationResolverService = accessor.get(IConfigurationResolverService); - const workspaceContextService = accessor.get(IWorkspaceContextService); const historyService = accessor.get(IHistoryService); const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; - const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text); terminalInstance.sendText(resolvedText, false); } From 904004b95f0bdf8db08a407d4f546e4cc26e57df Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Sun, 3 Feb 2019 16:21:53 +0100 Subject: [PATCH 0950/1066] fixes #67449 --- src/vs/base/browser/ui/tree/abstractTree.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 742c6aa0d8e..134044cfde3 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -386,6 +386,9 @@ class TypeFilterController implements IDisposable { private _pattern = ''; get pattern(): string { return this._pattern; } + private _filterOnType: boolean; + get filterOnType(): boolean { return this._filterOnType; } + private positionClassName = 'ne'; private domNode: HTMLElement; private messageDomNode: HTMLElement; @@ -416,9 +419,10 @@ class TypeFilterController implements IDisposable { this.labelDomNode = append(this.domNode, $('span.label')); const controls = append(this.domNode, $('.controls')); + this._filterOnType = !!tree.options.filterOnType; this.filterOnTypeDomNode = append(controls, $('input.filter')); this.filterOnTypeDomNode.type = 'checkbox'; - this.filterOnTypeDomNode.checked = !!tree.options.filterOnType; + this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; this.updateFilterOnTypeTitle(); domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); @@ -440,7 +444,11 @@ class TypeFilterController implements IDisposable { this.enable(); } - this.filterOnTypeDomNode.checked = !!options.filterOnType; + if (typeof options.filterOnType !== 'undefined') { + this._filterOnType = !!options.filterOnType; + this.filterOnTypeDomNode.checked = this._filterOnType; + } + this.automaticKeyboardNavigation = typeof options.automaticKeyboardNavigation === 'undefined' ? true : options.automaticKeyboardNavigation; this.tree.refilter(); this.render(); @@ -619,7 +627,7 @@ class TypeFilterController implements IDisposable { } private updateFilterOnTypeTitle(): void { - if (this.filterOnTypeDomNode.checked) { + if (this.filterOnType) { this.filterOnTypeDomNode.title = localize('disable filter on type', "Disable Filter on Type"); } else { this.filterOnTypeDomNode.title = localize('enable filter on type', "Enable Filter on Type"); @@ -928,7 +936,7 @@ export abstract class AbstractTree implements IDisposable if (_options.keyboardNavigationLabelProvider) { this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); this.focusNavigationFilter = node => { - if (!this.typeFilterController!.enabled || !this.typeFilterController!.pattern) { + if (!this.typeFilterController!.enabled || !this.typeFilterController!.pattern || this.typeFilterController!.filterOnType) { return true; } From 842b2ede43066445a0721d410cad004499731cd5 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Sun, 3 Feb 2019 16:38:10 +0100 Subject: [PATCH 0951/1066] fixes #67733 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ++++ .../parts/files/electron-browser/views/explorerView.ts | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index bfe2b0e6161..31e5b0af409 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -412,6 +412,10 @@ export class AsyncDataTree implements IDisposable await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } + hasNode(element: T): boolean { + return this.renderedNodes.has(element); + } + // View refresh(element: T): void { 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 247910d03e8..f42a9d00cce 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/explorerView.ts @@ -430,6 +430,12 @@ export class ExplorerView extends ViewletPanel { this.shouldRefresh = true; return Promise.resolve(undefined); } + + // Tree node doesn't exist yet + if (item && !this.tree.hasNode(item)) { + return Promise.resolve(undefined); + } + const recursive = !item; const toRefresh = item || this.tree.getInput(); From 427927db8c1eb68bed288c7bcfd852b11e7f4af2 Mon Sep 17 00:00:00 2001 From: al Date: Sun, 3 Feb 2019 23:05:35 +0300 Subject: [PATCH 0952/1066] Fix wrong hover on empty when column --- src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 72e4cd23c34..8d358e31f2c 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -926,6 +926,7 @@ class WhenColumn extends Column { whenLabel.element.title = keybindingItemEntry.keybindingItem.when; } else { this.whenColumn.textContent = '—'; + this.whenColumn.title = ''; } } From d562c85b42646bbf08e661f3ab68970ce87a40a1 Mon Sep 17 00:00:00 2001 From: Hao Hu Date: Sun, 3 Feb 2019 23:22:33 -0800 Subject: [PATCH 0953/1066] Add git diff terminal link handler Fixes #67238 --- .../electron-browser/terminalLinkHandler.ts | 69 ++++++++++++------- .../terminalLinkHandler.test.ts | 32 +++++++++ 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts index 31a1d6fc8ed..993b6e548fa 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalLinkHandler.ts @@ -63,6 +63,9 @@ export class TerminalLinkHandler { private _widgetManager: TerminalWidgetManager; private _processCwd: string; private _localLinkPattern: RegExp; + private _gitDiffPreImagePattern: RegExp; + private _gitDiffPostImagePattern: RegExp; + private readonly _tooltipCallback: (event: MouseEvent, uri: string) => boolean | void; constructor( private _xterm: any, @@ -75,8 +78,23 @@ export class TerminalLinkHandler { const baseLocalLinkClause = _platform === platform.Platform.Windows ? winLocalLinkClause : unixLocalLinkClause; // Append line and column number regex this._localLinkPattern = new RegExp(`${baseLocalLinkClause}(${lineAndColumnClause})`); + // Matches '--- a/src/file1', capturing 'src/file1' in group 1 + this._gitDiffPreImagePattern = /^--- a\/(\S*)/; + // Matches '+++ b/src/file1', capturing 'src/file1' in group 1 + this._gitDiffPostImagePattern = /^\+\+\+ b\/(\S*)/; + + this._tooltipCallback = (e: MouseEvent) => { + if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') { + const target = (e.target as HTMLElement); + this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString()); + } else { + this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString()); + } + }; + this.registerWebLinkHandler(); this.registerLocalLinkHandler(); + this.registerGitDiffLinkHandlers(); } public setWidgetManager(widgetManager: TerminalWidgetManager): void { @@ -90,14 +108,7 @@ export class TerminalLinkHandler { public registerCustomLinkHandler(regex: RegExp, handler: (uri: string) => void, matchIndex?: number, validationCallback?: XtermLinkMatcherValidationCallback): number { const options: ILinkMatcherOptions = { matchIndex, - tooltipCallback: (e: MouseEvent) => { - if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') { - const target = (e.target as HTMLElement); - this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString()); - } else { - this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString()); - } - }, + tooltipCallback: this._tooltipCallback, leaveCallback: () => this._widgetManager.closeMessage(), willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), priority: CUSTOM_LINK_PRIORITY @@ -114,14 +125,7 @@ export class TerminalLinkHandler { }); this._xterm.webLinksInit(wrappedHandler, { validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateWebLink(uri, callback), - tooltipCallback: (e: MouseEvent) => { - if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') { - const target = (e.target as HTMLElement); - this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString()); - } else { - this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString()); - } - }, + tooltipCallback: this._tooltipCallback, leaveCallback: () => this._widgetManager.closeMessage(), willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e) }); @@ -133,20 +137,29 @@ export class TerminalLinkHandler { }); this._xterm.registerLinkMatcher(this._localLinkRegex, wrappedHandler, { validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback), - tooltipCallback: (e: MouseEvent) => { - if (this._terminalService && this._terminalService.configHelper.config.rendererType === 'dom') { - const target = (e.target as HTMLElement); - this._widgetManager.showMessage(target.offsetLeft, target.offsetTop, this._getLinkHoverString()); - } else { - this._widgetManager.showMessage(e.offsetX, e.offsetY, this._getLinkHoverString()); - } - }, + tooltipCallback: this._tooltipCallback, leaveCallback: () => this._widgetManager.closeMessage(), willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), priority: LOCAL_LINK_PRIORITY }); } + public registerGitDiffLinkHandlers(): void { + const wrappedHandler = this._wrapLinkHandler(url => { + this._handleLocalLink(url); + }); + const options = { + matchIndex: 1, + validationCallback: (uri: string, callback: (isValid: boolean) => void) => this._validateLocalLink(uri, callback), + tooltipCallback: this._tooltipCallback, + leaveCallback: () => this._widgetManager.closeMessage(), + willLinkActivate: (e: MouseEvent) => this._isLinkActivationModifierDown(e), + priority: LOCAL_LINK_PRIORITY + }; + this._xterm.registerLinkMatcher(this._gitDiffPreImagePattern, wrappedHandler, options); + this._xterm.registerLinkMatcher(this._gitDiffPostImagePattern, wrappedHandler, options); + } + public dispose(): void { this._xterm = null; this._hoverDisposables = dispose(this._hoverDisposables); @@ -172,6 +185,14 @@ export class TerminalLinkHandler { return this._localLinkPattern; } + protected get _gitDiffPreImageRegex(): RegExp { + return this._gitDiffPreImagePattern; + } + + protected get _gitDiffPostImageRegex(): RegExp { + return this._gitDiffPostImagePattern; + } + private _handleLocalLink(link: string): PromiseLike { return this._resolvePath(link).then(resolvedLink => { if (!resolvedLink) { diff --git a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts index 1cc1487e224..a15fe4cf18d 100644 --- a/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts +++ b/src/vs/workbench/parts/terminal/test/electron-browser/terminalLinkHandler.test.ts @@ -14,6 +14,12 @@ class TestTerminalLinkHandler extends TerminalLinkHandler { public get localLinkRegex(): RegExp { return this._localLinkRegex; } + public get gitDiffLinkPreImageRegex(): RegExp { + return this._gitDiffPreImageRegex; + } + public get gitDiffLinkPostImageRegex(): RegExp { + return this._gitDiffPostImageRegex; + } public preprocessPath(link: string): string | null { return this._preprocessPath(link); } @@ -217,4 +223,30 @@ suite('Workbench - TerminalLinkHandler', () => { assert.equal(linkHandler.preprocessPath('/absolute/path/file3'), '/absolute/path/file3'); }); }); + + test('gitDiffLinkRegex', () => { + // The platform is irrelevant because the links generated by Git are the same format regardless of platform + const linkHandler = new TestTerminalLinkHandler(new TestXterm(), Platform.Linux, null!, null!, null!, null!); + + function assertAreGoodMatches(matches: RegExpMatchArray | null) { + if (matches) { + assert.equal(matches.length, 2); + assert.equal(matches[1], 'src/file1'); + } else { + assert.fail(); + } + } + + // Happy cases + assertAreGoodMatches('--- a/src/file1'.match(linkHandler.gitDiffLinkPreImageRegex)); + assertAreGoodMatches('--- a/src/file1 '.match(linkHandler.gitDiffLinkPreImageRegex)); + assertAreGoodMatches('+++ b/src/file1'.match(linkHandler.gitDiffLinkPostImageRegex)); + assertAreGoodMatches('+++ b/src/file1 '.match(linkHandler.gitDiffLinkPostImageRegex)); + + // Make sure /dev/null isn't a match + assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null'), false); + assert.equal(linkHandler.gitDiffLinkPreImageRegex.test('--- /dev/null '), false); + assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null'), false); + assert.equal(linkHandler.gitDiffLinkPostImageRegex.test('+++ /dev/null '), false); + }); }); From 7492c99a8b8ce5beca47e346531a41b88f9483a3 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 1 Feb 2019 14:34:50 +0100 Subject: [PATCH 0954/1066] add `strings.removeAccents` #66720 --- src/vs/base/common/strings.ts | 15 +++++++ src/vs/base/test/common/strings.test.ts | 55 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 39da4f5446f..72a0c0785b5 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -627,6 +627,21 @@ export function removeAnsiEscapeCodes(str: string): string { return str; } +export const removeAccents: (str: string) => string = (function () { + if (typeof (String.prototype as any).normalize !== 'function') { + // ☹️ no ES6 features... + return function (str: string) { return str; }; + } else { + // transform into NFD form and remove accents + // see: https://stackoverflow.com/questions/990904/remove-accents-diacritics-in-a-string-in-javascript/37511463#37511463 + const regex = /[\u0300-\u036f]/g; + return function (str: string) { + return (str as any).normalize('NFD').replace(regex, empty); + }; + } +})(); + + // -- UTF-8 BOM export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 758bfb246d1..015075a90a9 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -403,4 +403,59 @@ suite('Strings', () => { assert.equal(strings.getNLines('foo\nbar\nsomething', 2), 'foo\nbar'); assert.equal(strings.getNLines('foo', 0), ''); }); + + test('removeAccents', function () { + assert.equal(strings.removeAccents('joào'), 'joao'); + assert.equal(strings.removeAccents('joáo'), 'joao'); + assert.equal(strings.removeAccents('joâo'), 'joao'); + assert.equal(strings.removeAccents('joäo'), 'joao'); + // assert.equal(strings.removeAccents('joæo'), 'joao'); // not an accent + assert.equal(strings.removeAccents('joão'), 'joao'); + assert.equal(strings.removeAccents('joåo'), 'joao'); + assert.equal(strings.removeAccents('joåo'), 'joao'); + assert.equal(strings.removeAccents('joāo'), 'joao'); + + assert.equal(strings.removeAccents('fôo'), 'foo'); + assert.equal(strings.removeAccents('föo'), 'foo'); + assert.equal(strings.removeAccents('fòo'), 'foo'); + assert.equal(strings.removeAccents('fóo'), 'foo'); + // assert.equal(strings.removeAccents('fœo'), 'foo'); + // assert.equal(strings.removeAccents('føo'), 'foo'); + assert.equal(strings.removeAccents('fōo'), 'foo'); + assert.equal(strings.removeAccents('fõo'), 'foo'); + + assert.equal(strings.removeAccents('andrè'), 'andre'); + assert.equal(strings.removeAccents('andré'), 'andre'); + assert.equal(strings.removeAccents('andrê'), 'andre'); + assert.equal(strings.removeAccents('andrë'), 'andre'); + assert.equal(strings.removeAccents('andrē'), 'andre'); + assert.equal(strings.removeAccents('andrė'), 'andre'); + assert.equal(strings.removeAccents('andrę'), 'andre'); + + assert.equal(strings.removeAccents('hvîc'), 'hvic'); + assert.equal(strings.removeAccents('hvïc'), 'hvic'); + assert.equal(strings.removeAccents('hvíc'), 'hvic'); + assert.equal(strings.removeAccents('hvīc'), 'hvic'); + assert.equal(strings.removeAccents('hvįc'), 'hvic'); + assert.equal(strings.removeAccents('hvìc'), 'hvic'); + + assert.equal(strings.removeAccents('ûdo'), 'udo'); + assert.equal(strings.removeAccents('üdo'), 'udo'); + assert.equal(strings.removeAccents('ùdo'), 'udo'); + assert.equal(strings.removeAccents('údo'), 'udo'); + assert.equal(strings.removeAccents('ūdo'), 'udo'); + + assert.equal(strings.removeAccents('heÿ'), 'hey'); + + // assert.equal(strings.removeAccents('gruß'), 'grus'); + assert.equal(strings.removeAccents('gruś'), 'grus'); + assert.equal(strings.removeAccents('gruš'), 'grus'); + + assert.equal(strings.removeAccents('çool'), 'cool'); + assert.equal(strings.removeAccents('ćool'), 'cool'); + assert.equal(strings.removeAccents('čool'), 'cool'); + + assert.equal(strings.removeAccents('ñice'), 'nice'); + assert.equal(strings.removeAccents('ńice'), 'nice'); + }); }); From 3646ede2d17413848b3d7368c7d1b1c87e011fc5 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 1 Feb 2019 16:48:19 +0100 Subject: [PATCH 0955/1066] add lib.es2018.promise.d.to to yet another place, #65040 --- build/gulpfile.editor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index e59be13ea3e..3fc992bd9c4 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -84,6 +84,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function () { 'typings/thenable.d.ts', 'typings/es6-promise.d.ts', 'typings/require-monaco.d.ts', + "typings/lib.es2018.promise.d.ts", 'vs/monaco.d.ts' ], libs: [ From 67ffab8dcd1a6752d8b62bcd13d7020101eef568 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 1 Feb 2019 18:26:53 +0100 Subject: [PATCH 0956/1066] use SIGUSR1 to enable inspector on extension host --- .../electron-browser/extensionHost.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index b54507fb37e..b5bce66b0f2 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -224,9 +224,14 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Print out extension host output onDebouncedOutput(output => { - const inspectorUrlMatch = !this._environmentService.isBuilt && output.data && output.data.match(/ws:\/\/([^\s]+)/); + const inspectorUrlMatch = output.data && output.data.match(/ws:\/\/([^\s]+:(\d+)\/[^\s]+)/); if (inspectorUrlMatch) { - console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black'); + if (!this._environmentService.isBuilt) { + console.log(`%c[Extension Host] %cdebugger inspector at chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=${inspectorUrlMatch[1]}`, 'color: blue', 'color: black'); + } + if (!this._inspectPort) { + this._inspectPort = Number(inspectorUrlMatch[2]); + } } else { console.group('Extension Host'); console.log(output.data, ...output.format); @@ -498,6 +503,15 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { } } + public enableInspector(): Promise { + if (this._inspectPort) { + return Promise.resolve(); + } + // send SIGUSR1 and wait a little + this._extensionHostProcess.kill('SIGUSR1'); + return timeout(1000); + } + public getInspectPort(): number { return this._inspectPort; } From 29357370fbcb5c432ed2b321abf3355120d8f0b9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 09:49:04 +0100 Subject: [PATCH 0957/1066] debt - avoid dependencies from files to other parts (part 1) --- src/vs/workbench/parts/files/common/files.ts | 5 ----- .../electron-browser/fileActions.contribution.ts | 11 ----------- .../parts/preferences/browser/preferencesEditor.ts | 3 +-- .../electron-browser/preferences.contribution.ts | 13 ++++++++++++- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/files/common/files.ts b/src/vs/workbench/parts/files/common/files.ts index 8915bd13b1d..9b8cbdf5d29 100644 --- a/src/vs/workbench/parts/files/common/files.ts +++ b/src/vs/workbench/parts/files/common/files.ts @@ -91,11 +91,6 @@ export const OpenEditorsVisibleCondition = ContextKeyExpr.has(openEditorsVisible export const FilesExplorerFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(explorerViewletVisibleId), ContextKeyExpr.has(filesExplorerFocusId), ContextKeyExpr.not(InputFocusedContextKey)); export const ExplorerFocusCondition = ContextKeyExpr.and(ContextKeyExpr.has(explorerViewletVisibleId), ContextKeyExpr.has(explorerViewletFocusId), ContextKeyExpr.not(InputFocusedContextKey)); -/** - * Preferences editor id. - */ -export const PREFERENCES_EDITOR_ID = 'workbench.editor.preferencesEditor'; - /** * Text file editor id. */ 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 ca9a038f774..99ad4630a14 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -18,7 +18,6 @@ import { isWindows, isMacintosh } from 'vs/base/common/platform'; 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'; import { AutoSaveContext } from 'vs/workbench/services/textfile/common/textfiles'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; @@ -484,16 +483,6 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { when: ContextKeyExpr.and(ExplorerRootContext) }); -MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { - group: '2_workspace', - order: 20, - command: { - id: OPEN_FOLDER_SETTINGS_COMMAND, - title: OPEN_FOLDER_SETTINGS_LABEL - }, - when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext) -}); - MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { group: '2_workspace', order: 30, diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index b104b1b3b94..8e8da3a6699 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -44,7 +44,6 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorInput, EditorOptions, IEditorControl } from 'vs/workbench/common/editor'; import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; -import { PREFERENCES_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { DefaultSettingsRenderer, FolderSettingsRenderer, IPreferencesRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; import { SearchWidget, SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, IPreferencesSearchService, ISearchProvider, CONTEXT_SETTINGS_JSON_EDITOR } from 'vs/workbench/parts/preferences/common/preferences'; @@ -58,7 +57,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; export class PreferencesEditor extends BaseEditor { - static readonly ID: string = PREFERENCES_EDITOR_ID; + static readonly ID: string = 'workbench.editor.preferencesEditor'; private defaultSettingsEditorContextKey: IContextKey; private defaultSettingsJSONEditorContextKey: IContextKey; diff --git a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts index 8b06f6018e1..3e43d22c5e7 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/preferences.contribution.ts @@ -27,7 +27,7 @@ import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchCo import { EditorInput, Extensions as EditorInputExtensions, IEditorInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { KeybindingsEditor } from 'vs/workbench/parts/preferences/browser/keybindingsEditor'; -import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { ConfigureLanguageBasedSettingsAction, OpenDefaultKeybindingsFileAction, OpenFolderSettingsAction, OpenGlobalKeybindingsAction, OpenGlobalKeybindingsFileAction, OpenGlobalSettingsAction, OpenRawDefaultSettingsAction, OpenSettings2Action, OpenSettingsJsonAction, OpenWorkspaceSettingsAction, OPEN_FOLDER_SETTINGS_COMMAND, OPEN_FOLDER_SETTINGS_LABEL } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, CONTEXT_KEYBINDING_FOCUS, CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_JSON_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IKeybindingsEditor, IPreferencesSearchService, KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, KEYBINDINGS_EDITOR_COMMAND_COPY, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_FOCUS_KEYBINDINGS, KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_SEARCH, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS, KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, SETTINGS_EDITOR_COMMAND_EDIT_FOCUSED_SETTING, SETTINGS_EDITOR_COMMAND_FILTER_MODIFIED, SETTINGS_EDITOR_COMMAND_FILTER_ONLINE, SETTINGS_EDITOR_COMMAND_FOCUS_FILE, SETTINGS_EDITOR_COMMAND_FOCUS_NEXT_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_PREVIOUS_SETTING, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_FROM_SEARCH, SETTINGS_EDITOR_COMMAND_FOCUS_SETTINGS_LIST, SETTINGS_EDITOR_COMMAND_SEARCH, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, SETTINGS_EDITOR_COMMAND_SWITCH_TO_JSON, SETTINGS_COMMAND_OPEN_SETTINGS } from 'vs/workbench/parts/preferences/common/preferences'; import { PreferencesContribution } from 'vs/workbench/parts/preferences/common/preferencesContribution'; @@ -36,6 +36,7 @@ import { SettingsEditor2 } from 'vs/workbench/parts/preferences/electron-browser import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; +import { ExplorerRootContext, ExplorerFolderContext } from 'vs/workbench/parts/files/common/files'; registerSingleton(IPreferencesSearchService, PreferencesSearchService, true); @@ -773,3 +774,13 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { CONTEXT_SETTINGS_JSON_EDITOR.toNegated() ) }); + +MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { + group: '2_workspace', + order: 20, + command: { + id: OPEN_FOLDER_SETTINGS_COMMAND, + title: OPEN_FOLDER_SETTINGS_LABEL + }, + when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext) +}); \ No newline at end of file From dcea90230b46a2ef7cd27305ed9e68563ce6edec Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 4 Feb 2019 09:50:05 +0100 Subject: [PATCH 0958/1066] add comment --- .../services/extensions/electron-browser/extensionHost.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index b5bce66b0f2..1749a27e1a6 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -507,7 +507,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { if (this._inspectPort) { return Promise.resolve(); } - // send SIGUSR1 and wait a little + // send SIGUSR1 and wait a little the actual port is read from the process stdout which we + // scan here: https://github.com/Microsoft/vscode/blob/67ffab8dcd1a6752d8b62bcd13d7020101eef568/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts#L225-L240 this._extensionHostProcess.kill('SIGUSR1'); return timeout(1000); } From ed856d85f5916b690490ef8f2c81e4d7bb34da75 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 09:50:26 +0100 Subject: [PATCH 0959/1066] Revert "disable save actions for webview" This reverts commit c3b9299c9d68bb566d946e8f7260177471be8de3. --- .../files/electron-browser/fileActions.contribution.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 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 99ad4630a14..3a75d49e119 100644 --- a/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/electron-browser/fileActions.contribution.ts @@ -24,8 +24,6 @@ import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listServi import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor'; -import { WalkThroughPart } from 'vs/workbench/parts/welcome/walkThrough/electron-browser/walkThroughPart'; // Contribute Global Actions const category = nls.localize('filesCategory', "File"); @@ -550,8 +548,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '4_save', command: { id: SAVE_FILE_COMMAND_ID, - title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save"), - precondition: ContextKeyExpr.and(ContextKeyExpr.notEquals('activeEditor', WebviewEditor.ID), ContextKeyExpr.notEquals('activeEditor', WalkThroughPart.ID)) + title: nls.localize({ key: 'miSave', comment: ['&& denotes a mnemonic'] }, "&&Save") }, order: 1 }); @@ -560,8 +557,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '4_save', command: { id: SAVE_FILE_AS_COMMAND_ID, - title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As..."), - precondition: ContextKeyExpr.and(ContextKeyExpr.notEquals('activeEditor', WebviewEditor.ID), ContextKeyExpr.notEquals('activeEditor', WalkThroughPart.ID)) + title: nls.localize({ key: 'miSaveAs', comment: ['&& denotes a mnemonic'] }, "Save &&As...") }, order: 2 }); From a455381dd18af6eb7e35284fa8324728e520accb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 09:55:28 +0100 Subject: [PATCH 0960/1066] debt - disallow cross-part dependencies for files part --- .../test/browser/fileEditorInput.test.ts | 1 + tslint.json | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts index 54c12010023..2e5676deab9 100644 --- a/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/parts/files/test/browser/fileEditorInput.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { join } from 'vs/base/common/paths'; diff --git a/tslint.json b/tslint.json index 67a640687c9..3d7876e972d 100644 --- a/tslint.json +++ b/tslint.json @@ -457,6 +457,54 @@ "*" // node modules ] }, + { + "target": "**/vs/workbench/parts/files/common/**", + "restrictions": [ + "vs/nls", + "**/vs/base/common/**", + "**/vs/base/parts/*/common/**", + "**/vs/platform/*/common/**", + "**/vs/editor/common/**", + "**/vs/editor/contrib/*/common/**", + "**/vs/workbench/common/**", + "**/vs/workbench/services/*/common/**", + "**/vs/workbench/parts/files/common/**", + "assert" + ] + }, + { + "target": "**/vs/workbench/parts/files/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser}/**", + "**/vs/base/parts/*/{common,browser}/**", + "**/vs/platform/*/{common,browser}/**", + "**/vs/editor/{common,browser}/**", + "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/{common,browser}/**", + "**/vs/workbench/services/*/{common,browser}/**", + "**/vs/workbench/parts/files/{common,browser}/**", + "assert" + ] + }, + { + "target": "**/vs/workbench/parts/files/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/vs/base/{common,browser,node,electron-browser}/**", + "**/vs/base/parts/*/{common,browser,node,electron-browser}/**", + "**/vs/platform/node/**", + "**/vs/platform/*/{common,browser,node,electron-browser}/**", + "**/vs/editor/{common,browser,node,electron-browser}/**", + "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention + "**/vs/workbench/{common,browser,node,electron-browser,api}/**", + "**/vs/workbench/services/*/{common,browser,node,electron-browser}/**", + "**/vs/workbench/parts/files/{common,browser,node,electron-browser}/**", + "*" // node modules + ] + }, { "target": "**/vs/code/node/**", "restrictions": [ From 2b3f9fdcd1600562029cb9c436880cfa171f979d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 10:14:06 +0100 Subject: [PATCH 0961/1066] debt - guard replaceEditors() from replacing the same editor --- .../browser/parts/editor/editorGroupView.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 3a5fdebc6a3..edfd5b9aeb5 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -1308,11 +1308,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Open inactive editor this.doOpenEditor(replacement, options); - // Close replaced inactive edior - this.doCloseInactiveEditor(editor); - - // Forward to title control - this.titleAreaControl.closeEditor(editor); + // Close replaced inactive editor unless they match + if (!editor.matches(replacement)) { + this.doCloseInactiveEditor(editor); + this.titleAreaControl.closeEditor(editor); + } }); // Handle active last @@ -1321,11 +1321,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Open replacement as active editor const openEditorResult = this.doOpenEditor(activeReplacement.replacement, activeReplacement.options); - // Close previous active editor - this.doCloseInactiveEditor(activeReplacement.editor); - - // Forward to title control - this.titleAreaControl.closeEditor(activeReplacement.editor); + // Close replaced active editor unless they match + if (!activeReplacement.editor.matches(activeReplacement.replacement)) { + this.doCloseInactiveEditor(activeReplacement.editor); + this.titleAreaControl.closeEditor(activeReplacement.editor); + } return openEditorResult.then(() => undefined); } From 1ad028dfa956522dbd4ddabf5551092ca40fac19 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 10:15:08 +0100 Subject: [PATCH 0962/1066] part of #67695 --- src/vs/workbench/browser/actions/layoutActions.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 4ae06a85506..b6e13835191 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -337,7 +337,11 @@ 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', viewCategory); +registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { + primary: undefined, + mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }, + linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, } +}), 'View: Toggle Tab Visibility', viewCategory); // --- Toggle Zen Mode From 125c07d1481156ce5584c4a6b0d0dc8627dfe4fc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 10:32:09 +0100 Subject: [PATCH 0963/1066] debt - cleanup storage leftovers (#67837) --- src/vs/base/node/storage.ts | 21 --- .../storage/node/storageMainService.ts | 34 +---- .../platform/storage/node/storageService.ts | 53 +------- src/vs/workbench/electron-browser/shell.ts | 125 +----------------- .../electron-browser/perfviewEditor.ts | 2 - .../timer/electron-browser/timerService.ts | 20 --- 6 files changed, 10 insertions(+), 245 deletions(-) diff --git a/src/vs/base/node/storage.ts b/src/vs/base/node/storage.ts index 5cb7369ef99..fa14cb70e4e 100644 --- a/src/vs/base/node/storage.ts +++ b/src/vs/base/node/storage.ts @@ -10,7 +10,6 @@ import { ThrottledDelayer, timeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { mapToString, setToString } from 'vs/base/common/map'; import { basename } from 'path'; -import { mark } from 'vs/base/common/performance'; import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs'; import { fill } from 'vs/base/common/arrays'; @@ -326,8 +325,6 @@ export class SQLiteStorageDatabase implements IStorageDatabase { get onDidChangeItemsExternal(): Event { return Event.None; } // since we are the only client, there can be no external changes - private static measuredRequireDuration: boolean; // TODO@Ben remove me after a while - private static BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY private static MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement @@ -598,21 +595,8 @@ export class SQLiteStorageDatabase implements IStorageDatabase { } private doConnect(path: string): Promise { - - // TODO@Ben clean up performance markers return new Promise((resolve, reject) => { - let measureRequireDuration = false; - if (!SQLiteStorageDatabase.measuredRequireDuration) { - SQLiteStorageDatabase.measuredRequireDuration = true; - measureRequireDuration = true; - - mark('willRequireSQLite'); - } import('vscode-sqlite3').then(sqlite3 => { - if (measureRequireDuration) { - mark('didRequireSQLite'); - } - const connection: IDatabaseConnection = { db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { if (error) { @@ -622,17 +606,12 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // The following exec() statement serves two purposes: // - create the DB if it does not exist yet // - validate that the DB is not corrupt (the open() call does not throw otherwise) - mark('willSetupSQLiteSchema'); return this.exec(connection, [ 'PRAGMA user_version = 1;', 'CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB)' ].join('')).then(() => { - mark('didSetupSQLiteSchema'); - return resolve(connection); }, error => { - mark('didSetupSQLiteSchema'); - return connection.db.close(() => reject(error)); }); }), diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index 71f7299951f..72ae2f3222b 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -10,8 +10,7 @@ import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IStorage, Storage, SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions, InMemoryStorageDatabase } from 'vs/base/node/storage'; import { join } from 'path'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { mark, getDuration } from 'vs/base/common/performance'; +import { mark } from 'vs/base/common/performance'; import { exists, readdir } from 'vs/base/node/pfs'; import { Database } from 'vscode-sqlite3'; import { endsWith, startsWith } from 'vs/base/common/strings'; @@ -91,8 +90,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic constructor( @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @IEnvironmentService private readonly environmentService: IEnvironmentService ) { super(); @@ -109,27 +107,9 @@ export class StorageMainService extends Disposable implements IStorageMainServic } private createLogginOptions(): ISQLiteStorageDatabaseLoggingOptions { - const loggedStorageErrors = new Set(); - return { logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => { - this.logService.error(error); - - const errorStr = `${error}`; - if (!loggedStorageErrors.has(errorStr)) { - loggedStorageErrors.add(errorStr); - - /* __GDPR__ - "sqliteMainStorageError" : { - "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('sqliteMainStorageError', { - 'storageError': errorStr - }); - } - } + logError: error => this.logService.error(error) } as ISQLiteStorageDatabaseLoggingOptions; } @@ -375,18 +355,12 @@ export class StorageMainService extends Disposable implements IStorageMainServic } close(): Promise { - this.logService.trace('StorageMainService#close() - begin'); // Signal as event so that clients can still store data this._onWillSaveState.fire(); // Do it - mark('main:willCloseGlobalStorage'); - return this.storage.close().then(() => { - mark('main:didCloseGlobalStorage'); - - this.logService.trace(`StorageMainService#close() - finished in ${getDuration('main:willCloseGlobalStorage', 'main:didCloseGlobalStorage')}ms`); - }); + return this.storage.close(); } checkIntegrity(full: boolean): Promise { diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index feacaec0b54..536a6fcb0a3 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -30,27 +30,6 @@ export class StorageService extends Disposable implements IStorageService { private _onWillSaveState: Emitter = this._register(new Emitter()); get onWillSaveState(): Event { return this._onWillSaveState.event; } - private _hasErrors = false; - get hasErrors(): boolean { return this._hasErrors; } - - private bufferedWorkspaceStorageErrors?: Array = []; - private _onWorkspaceStorageError: Emitter = this._register(new Emitter()); - get onWorkspaceStorageError(): Event { - if (Array.isArray(this.bufferedWorkspaceStorageErrors)) { - // todo@ben cleanup after a while - if (this.bufferedWorkspaceStorageErrors.length > 0) { - const bufferedStorageErrors = this.bufferedWorkspaceStorageErrors; - setTimeout(() => { - this._onWorkspaceStorageError.fire(`[startup errors] ${bufferedStorageErrors.join('\n')}`); - }, 0); - } - - this.bufferedWorkspaceStorageErrors = undefined; - } - - return this._onWorkspaceStorageError.event; - } - private globalStorage: IStorage; private workspaceStoragePath: string; @@ -81,15 +60,7 @@ export class StorageService extends Disposable implements IStorageService { } private initializeGlobalStorage(): Promise { - mark('willInitGlobalStorage'); - - return this.globalStorage.init().then(() => { - mark('didInitGlobalStorage'); - }, error => { - mark('didInitGlobalStorage'); - - return Promise.reject(error); - }); + return this.globalStorage.init(); } private initializeWorkspaceStorage(payload: IWorkspaceInitializationPayload): Promise { @@ -120,17 +91,7 @@ export class StorageService extends Disposable implements IStorageService { // Logger for workspace storage const workspaceLoggingOptions: ISQLiteStorageDatabaseLoggingOptions = { logTrace: (this.logService.getLevel() === LogLevel.Trace) ? msg => this.logService.trace(msg) : undefined, - logError: error => { - this.logService.error(error); - - this._hasErrors = true; - - if (Array.isArray(this.bufferedWorkspaceStorageErrors)) { - this.bufferedWorkspaceStorageErrors.push(error); - } else { - this._onWorkspaceStorageError.fire(error); - } - } + logError: error => this.logService.error(error) }; // Dispose old (if any) @@ -219,14 +180,10 @@ export class StorageService extends Disposable implements IStorageService { this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN }); // Do it - mark('willCloseGlobalStorage'); - mark('willCloseWorkspaceStorage'); return Promise.all([ - this.globalStorage.close().then(() => mark('didCloseGlobalStorage')), - this.workspaceStorage.close().then(() => mark('didCloseWorkspaceStorage')) - ]).then(() => { - this.logService.trace(`[storage] closing took ${getDuration('willCloseGlobalStorage', 'didCloseGlobalStorage')}ms global / ${getDuration('willCloseWorkspaceStorage', 'didCloseWorkspaceStorage')}ms workspace`); - }); + this.globalStorage.close(), + this.workspaceStorage.close() + ]).then(() => undefined); } private getStorage(scope: StorageScope): IStorage { diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index fd959395067..ddd6d5862c7 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -74,7 +74,7 @@ import { IBroadcastService, BroadcastService } from 'vs/platform/broadcast/elect import { HashService } from 'vs/workbench/services/hash/node/hashService'; import { IHashService } from 'vs/workbench/services/hash/common/hashService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { StorageService } from 'vs/platform/storage/node/storageService'; import { Event, Emitter } from 'vs/base/common/event'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; @@ -215,11 +215,6 @@ export class Shell extends Disposable { // Startup Telemetry this.logStartupTelemetry(startupInfos); - - // Storage Telemetry (TODO@Ben remove me later, including storage errors) - if (!this.environmentService.extensionTestsPath) { - this.logStorageTelemetry(); - } }, error => handleStartupError(this.logService, error)); return workbench; @@ -273,124 +268,6 @@ export class Shell extends Disposable { perf.mark('didStartWorkbench'); } - private logStorageTelemetry(): void { - const initialStartup = !!this.configuration.isInitialStartup; - - const appReadyDuration = initialStartup ? perf.getDuration('main:started', 'main:appReady') : 0; - const workbenchReadyDuration = perf.getDuration(initialStartup ? 'main:started' : 'main:loadWindow', 'didStartWorkbench'); - const workspaceStorageRequireDuration = perf.getDuration('willRequireSQLite', 'didRequireSQLite'); - const workspaceStorageSchemaDuration = perf.getDuration('willSetupSQLiteSchema', 'didSetupSQLiteSchema'); - const globalStorageInitDurationMain = perf.getDuration('main:willInitGlobalStorage', 'main:didInitGlobalStorage'); - const globalStorageInitDuratioRenderer = perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage'); - const workspaceStorageInitDuration = perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'); - const workbenchLoadDuration = perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'); - - // Handle errors (avoid duplicates to reduce spam) - const loggedStorageErrors = new Set(); - this._register(this.storageService.onWorkspaceStorageError(error => { - const errorStr = `${error}`; - - if (!loggedStorageErrors.has(errorStr)) { - loggedStorageErrors.add(errorStr); - - /* __GDPR__ - "sqliteStorageError5" : { - "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - - /* __GDPR__ - "sqliteStorageError" : { - "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "storageError": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('sqliteStorageError5', { - 'appReadyTime': appReadyDuration, - 'workbenchReadyTime': workbenchReadyDuration, - 'workspaceRequireTime': workspaceStorageRequireDuration, - 'workspaceSchemaTime': workspaceStorageSchemaDuration, - 'globalReadTimeMain': globalStorageInitDurationMain, - 'globalReadTimeRenderer': globalStorageInitDuratioRenderer, - 'workspaceReadTime': workspaceStorageInitDuration, - 'workbenchRequireTime': workbenchLoadDuration, - 'workspaceKeys': this.storageService.getSize(StorageScope.WORKSPACE), - 'startupKind': this.lifecycleService.startupKind, - 'storageError': errorStr - }); - } - })); - - - if (this.storageService.hasErrors) { - return; // do not log performance numbers when errors occured - } - - if (this.environmentService.verbose) { - return; // do not log when running in verbose mode - } - - /* __GDPR__ - "sqliteStorageTimers5" : { - "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - - /* __GDPR__ - "sqliteStorageTimers" : { - "appReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchReadyTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceSchemaTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeMain" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "globalReadTimeRenderer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceReadTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workbenchRequireTime" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "workspaceKeys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "startupKind": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('sqliteStorageTimers5', { - 'appReadyTime': appReadyDuration, - 'workbenchReadyTime': workbenchReadyDuration, - 'workspaceRequireTime': workspaceStorageRequireDuration, - 'workspaceSchemaTime': workspaceStorageSchemaDuration, - 'globalReadTimeMain': globalStorageInitDurationMain, - 'globalReadTimeRenderer': globalStorageInitDuratioRenderer, - 'workspaceReadTime': workspaceStorageInitDuration, - 'workbenchRequireTime': workbenchLoadDuration, - 'workspaceKeys': this.storageService.getSize(StorageScope.WORKSPACE), - 'startupKind': this.lifecycleService.startupKind - }); - } private initServiceCollection(container: HTMLElement): [IInstantiationService, ServiceCollection] { const serviceCollection = new ServiceCollection(); diff --git a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts index 052ad299f86..016531777e8 100644 --- a/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/parts/performance/electron-browser/perfviewEditor.ts @@ -146,8 +146,6 @@ class PerfModelContentProvider implements ITextModelContentProvider { 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)]); table.push(['require(workbench.main.js)', metrics.timers.ellapsedRequire, '[renderer]', `cached data: ${(metrics.didUseCachedData ? 'YES' : 'NO')}${stats ? `, node_modules took ${stats.nodeRequireTotal}ms` : ''}`]); - table.push(['init global storage', metrics.timers.ellapsedGlobalStorageInitRenderer, '[renderer]', undefined]); - table.push(['require workspace storage', metrics.timers.ellapsedWorkspaceStorageRequire, '[renderer]', undefined]); table.push(['require & init workspace storage', metrics.timers.ellapsedWorkspaceStorageInit, '[renderer]', undefined]); table.push(['init workspace service', metrics.timers.ellapsedWorkspaceServiceInit, '[renderer]', undefined]); table.push(['register extensions & spawn extension host', metrics.timers.ellapsedExtensions, '[renderer]', undefined]); diff --git a/src/vs/workbench/services/timer/electron-browser/timerService.ts b/src/vs/workbench/services/timer/electron-browser/timerService.ts index c4a524d3706..1a4c78ecfa8 100644 --- a/src/vs/workbench/services/timer/electron-browser/timerService.ts +++ b/src/vs/workbench/services/timer/electron-browser/timerService.ts @@ -54,8 +54,6 @@ export interface IMemoryInfo { "timers.ellapsedExtensionsReady" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedGlobalStorageInitMain" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedGlobalStorageInitRenderer" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, - "timers.ellapsedWorkspaceStorageRequire" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceStorageInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedWorkspaceServiceInit" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, "timers.ellapsedViewletRestore" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true }, @@ -206,22 +204,6 @@ export interface IStartupMetrics { */ readonly ellapsedGlobalStorageInitMain: number; - /** - * The time it took to load the initial set of values from the global storage. - * - * * Happens in the renderer-process - * * Measured with the `willInitGlobalStorage` and `didInitGlobalStorage` performance marks. - */ - readonly ellapsedGlobalStorageInitRenderer: number; - - /** - * The time it took to require the workspace storage DB. - * - * * Happens in the renderer-process - * * Measured with the `willRequireSQLite` and `didRequireSQLite` performance marks. - */ - readonly ellapsedWorkspaceStorageRequire: number; - /** * The time it took to require the workspace storage DB, connect to it * and load the initial set of values. @@ -416,8 +398,6 @@ class TimerService implements ITimerService { ellapsedWindowLoadToRequire: perf.getDuration('main:loadWindow', 'willLoadWorkbenchMain'), ellapsedRequire: perf.getDuration('willLoadWorkbenchMain', 'didLoadWorkbenchMain'), ellapsedGlobalStorageInitMain: perf.getDuration('main:willInitGlobalStorage', 'main:didInitGlobalStorage'), - ellapsedGlobalStorageInitRenderer: perf.getDuration('willInitGlobalStorage', 'didInitGlobalStorage'), - ellapsedWorkspaceStorageRequire: perf.getDuration('willRequireSQLite', 'didRequireSQLite'), ellapsedWorkspaceStorageInit: perf.getDuration('willInitWorkspaceStorage', 'didInitWorkspaceStorage'), ellapsedWorkspaceServiceInit: perf.getDuration('willInitWorkspaceService', 'didInitWorkspaceService'), ellapsedExtensions: perf.getDuration('willLoadExtensions', 'didLoadExtensions'), From d7d55ee749c7ff15e538dc354130e078e8dc18b7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 10:35:32 +0100 Subject: [PATCH 0964/1066] fix build --- src/vs/workbench/browser/actions/layoutActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index b6e13835191..95e2db05534 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -338,7 +338,7 @@ class ToggleTabsVisibilityAction extends Action { } registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleTabsVisibilityAction, ToggleTabsVisibilityAction.ID, ToggleTabsVisibilityAction.LABEL, { - primary: undefined, + primary: undefined!, mac: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, }, linux: { primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KEY_W, } }), 'View: Toggle Tab Visibility', viewCategory); From c86667e5c067c37e30ad7aa8c192ecb5f40b3b65 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 10:40:14 +0100 Subject: [PATCH 0965/1066] debt - avoid constant settings lookup in lists --- src/vs/platform/list/browser/listService.ts | 75 +++++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 3b7b94de788..d49a2602d96 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -128,12 +128,27 @@ function useSingleClickToOpen(configurationService: IConfigurationService): bool return configurationService.getValue(openModeSettingKey) !== 'doubleClick'; } -class MultipleSelectionController implements IMultipleSelectionController { +class MultipleSelectionController extends Disposable implements IMultipleSelectionController { + private useAltAsMultipleSelectionModifier: boolean; - constructor(private configurationService: IConfigurationService) { } + constructor(private configurationService: IConfigurationService) { + super(); + + this.useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(configurationService); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(multiSelectModifierSettingKey)) { + this.useAltAsMultipleSelectionModifier = useAltAsMultipleSelectionModifier(this.configurationService); + } + })); + } isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { - if (useAltAsMultipleSelectionModifier(this.configurationService)) { + if (this.useAltAsMultipleSelectionModifier) { return event.browserEvent.altKey; } @@ -145,14 +160,29 @@ class MultipleSelectionController implements IMultipleSelectionController } } -class WorkbenchOpenController implements IOpenController { +class WorkbenchOpenController extends Disposable implements IOpenController { + private openOnSingleClick: boolean; - constructor(private configurationService: IConfigurationService, private existingOpenController?: IOpenController) { } + constructor(private configurationService: IConfigurationService, private existingOpenController?: IOpenController) { + super(); + + this.openOnSingleClick = useSingleClickToOpen(configurationService); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(openModeSettingKey)) { + this.openOnSingleClick = useSingleClickToOpen(this.configurationService); + } + })); + } shouldOpen(event: UIEvent): boolean { if (event instanceof MouseEvent) { const isDoubleClick = event.detail === 2; - if (!useSingleClickToOpen(this.configurationService) && !isDoubleClick) { + if (!this.openOnSingleClick && !isDoubleClick) { return false; } @@ -167,14 +197,19 @@ class WorkbenchOpenController implements IOpenController { } } -function toWorkbenchListOptions(options: IListOptions, configurationService: IConfigurationService, keybindingService: IKeybindingService): IListOptions { +function toWorkbenchListOptions(options: IListOptions, configurationService: IConfigurationService, keybindingService: IKeybindingService): [IListOptions, IDisposable] { + const disposables: IDisposable[] = []; const result = { ...options }; if (options.multipleSelectionSupport !== false && !options.multipleSelectionController) { - result.multipleSelectionController = new MultipleSelectionController(configurationService); + const multipleSelectionController = new MultipleSelectionController(configurationService); + result.multipleSelectionController = multipleSelectionController; + disposables.push(multipleSelectionController); } - result.openController = new WorkbenchOpenController(configurationService, options.openController); + const openController = new WorkbenchOpenController(configurationService, options.openController); + result.openController = openController; + disposables.push(openController); if (options.keyboardNavigationLabelProvider) { const tlp = options.keyboardNavigationLabelProvider; @@ -185,7 +220,7 @@ function toWorkbenchListOptions(options: IListOptions, configurationServic }; } - return result; + return [result, combinedDisposable(disposables)]; } let sharedListStyleSheet: HTMLStyleElement; @@ -241,17 +276,20 @@ export class WorkbenchList extends List { @IKeybindingService keybindingService: IKeybindingService ) { const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService), + ...workbenchListOptions, horizontalScrolling } as IListOptions ); + this.disposables.push(workbenchListOptionsDisposable); + this.contextKeyService = createScopedContextKeyService(contextKeyService, this); this.configurationService = configurationService; @@ -951,12 +989,13 @@ export class WorkbenchObjectTree, TFilterData = void> const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const openOnSingleClick = useSingleClickToOpen(configurationService); + const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService), + ...workbenchListOptions, indent: configurationService.getValue(treeIndentKey), automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', @@ -966,6 +1005,8 @@ export class WorkbenchObjectTree, TFilterData = void> keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) }); + this.disposables.push(workbenchListOptionsDisposable); + this.contextKeyService = createScopedContextKeyService(contextKeyService, this); const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); @@ -1067,12 +1108,13 @@ export class WorkbenchDataTree extends DataTree(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const openOnSingleClick = useSingleClickToOpen(configurationService); + const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(container, delegate, renderers, dataSource, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService), + ...workbenchListOptions, indent: configurationService.getValue(treeIndentKey), automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', @@ -1082,6 +1124,8 @@ export class WorkbenchDataTree extends DataTree extends Async const keyboardNavigation = configurationService.getValue(keyboardNavigationSettingKey); const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); const openOnSingleClick = useSingleClickToOpen(configurationService); + const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(container, delegate, renderers, dataSource, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService), + ...workbenchListOptions, indent: configurationService.getValue(treeIndentKey), automaticKeyboardNavigation, simpleKeyboardNavigation: keyboardNavigation === 'simple', @@ -1193,6 +1238,8 @@ export class WorkbenchAsyncDataTree extends Async keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService) }); + this.disposables.push(workbenchListOptionsDisposable); + this.contextKeyService = createScopedContextKeyService(contextKeyService, this); const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService); From b306c55a05062b8fe6ab1a80f19fc18870d87bbd Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 4 Feb 2019 09:33:54 +0100 Subject: [PATCH 0966/1066] Remove "Experimental setting" from proxy support setting (fixes #67832) --- src/vs/platform/request/node/request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/request/node/request.ts b/src/vs/platform/request/node/request.ts index 057969a072b..8602bb0357c 100644 --- a/src/vs/platform/request/node/request.ts +++ b/src/vs/platform/request/node/request.ts @@ -57,7 +57,7 @@ Registry.as(Extensions.Configuration) localize('proxySupportOverride', "Enable proxy support for extensions, override request options."), ], default: 'override', - description: localize('proxySupport', "Experimental setting: Use the proxy support for extensions.") + description: localize('proxySupport', "Use the proxy support for extensions.") } } }); From b940d15ed984b38f652c7eae86ce15f70cd8371d Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Mon, 4 Feb 2019 10:57:36 +0100 Subject: [PATCH 0967/1066] Update distro commit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 561f2d5c494..5f78d0e4027 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.32.0", - "distro": "cb7599e6323871114d034681abb30ed5e2066d3c", + "distro": "d274a23e463c080a0a3ba6278d43431d187e05f5", "author": { "name": "Microsoft Corporation" }, From f3552ec84d58edf2a3f69552e6df617936ebcb99 Mon Sep 17 00:00:00 2001 From: Alexandr Fadeev <5967447+fadeevab@users.noreply.github.com> Date: Mon, 4 Feb 2019 12:11:03 +0200 Subject: [PATCH 0968/1066] Makefile grammar updating: tests are updated about the handling of @, - and +. (#65629) 1. @, - and + in the beginning of recipes are colored. 2. Shell in recipes is not colored by shellscript extension anymore: improper colorizing of Makefile variables, low suitability. --- .../make/test/colorize-fixtures/makefile | 10 +- .../make/test/colorize-results/makefile.json | 261 +++++++++++++++++- 2 files changed, 264 insertions(+), 7 deletions(-) diff --git a/extensions/make/test/colorize-fixtures/makefile b/extensions/make/test/colorize-fixtures/makefile index 9eab44f6848..65fa3497669 100644 --- a/extensions/make/test/colorize-fixtures/makefile +++ b/extensions/make/test/colorize-fixtures/makefile @@ -1,5 +1,5 @@ .PHONY: all -all: hello +all: echo hello .PHONY: hello hello: main.o factorial.o \ @@ -25,8 +25,8 @@ hello.o: hello.cpp \ clean: rm *o hello -.PHONY: all -all: +.PHONY: var +var: # "$$" in a shell means to escape makefile's variable substitution. some_shell_var=$$(sed -nre 's/some regex with (group)/\1/p') @@ -34,6 +34,10 @@ all: echo: echo "#" and '#' in quotes are not comments \ and '\' will be continued + @echo Shell is not printed out, just a message. + @-+-+echo Error will be ignored here; invalidcommand + # And we can see variables are highlited as supposed to be: + @echo '$(CC) $(shell echo "123") -o $@' define defined $(info Checking existance of $(1) $(flavor $(1))) diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index b5d915ffca2..ec834fa59a5 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -55,7 +55,7 @@ } }, { - "c": " hello", + "c": " echo hello", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile", "r": { "dark_plus": "default: #D4D4D4", @@ -715,7 +715,7 @@ } }, { - "c": " all", + "c": " var", "t": "source.makefile meta.scope.target.makefile meta.scope.prerequisites.makefile", "r": { "dark_plus": "default: #D4D4D4", @@ -726,7 +726,7 @@ } }, { - "c": "all", + "c": "var", "t": "source.makefile meta.scope.target.makefile entity.name.function.target.makefile", "r": { "dark_plus": "entity.name.function: #DCDCAA", @@ -901,6 +901,259 @@ "hc_black": "default: #FFFFFF" } }, + { + "c": "\t", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.makefile meta.scope.recipe.makefile keyword.control.@.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "echo Shell is not printed out, just a message.", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@-+-+", + "t": "source.makefile meta.scope.recipe.makefile keyword.control.@-+-+.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "echo Error will be ignored here; invalidcommand", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "\t", + "t": "source.makefile punctuation.whitespace.comment.leading.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " And we can see variables are highlited as supposed to be:", + "t": "source.makefile comment.line.number-sign.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "\t", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "@", + "t": "source.makefile meta.scope.recipe.makefile keyword.control.@.makefile", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0" + } + }, + { + "c": "echo '", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "CC", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " ", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "shell", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.shell.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " echo \"123\"", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.makefile meta.scope.recipe.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " -o ", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$@", + "t": "source.makefile meta.scope.recipe.makefile variable.language.makefile", + "r": { + "dark_plus": "variable.language: #569CD6", + "light_plus": "variable.language: #0000FF", + "dark_vs": "variable.language: #569CD6", + "light_vs": "variable.language: #0000FF", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "'", + "t": "source.makefile meta.scope.recipe.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, { "c": "define", "t": "source.makefile meta.scope.conditional.makefile keyword.control.define.makefile", @@ -2760,4 +3013,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] From 8704b574da6c72848f74b3a88c47b2ede77e7a89 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 4 Feb 2019 11:15:22 +0100 Subject: [PATCH 0969/1066] build: decouple upload-vscode-sourcemaps task --- build/azure-pipelines/darwin/product-build-darwin.yml | 3 ++- build/gulpfile.vscode.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index ff9d32b35ca..cdd8dfb9c89 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -23,7 +23,8 @@ steps: set -e VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - yarn gulp -- vscode-darwin-min upload-vscode-sourcemaps + yarn gulp -- vscode-darwin-min + yarn gulp -- upload-vscode-sourcemaps displayName: Build - script: | diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2a39f8196bf..0bf3c4a38d9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -517,7 +517,7 @@ gulp.task('vscode-translations-import', function () { // Sourcemaps -gulp.task('upload-vscode-sourcemaps', ['vscode-darwin-min', 'minify-vscode'], () => { +gulp.task('upload-vscode-sourcemaps', () => { const vs = gulp.src('out-vscode-min/**/*.map', { base: 'out-vscode-min' }) .pipe(es.mapSync(f => { f.path = `${f.base}/core/${f.relative}`; From 6db6919846fc04ced7769f29e2e816df03ffd341 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 4 Feb 2019 11:21:12 +0100 Subject: [PATCH 0970/1066] rename --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 31e5b0af409..3890deafa57 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -260,7 +260,7 @@ export class AsyncDataTree implements IDisposable private readonly tree: ObjectTree, TFilterData>; private readonly root: IAsyncDataTreeNode; - private readonly renderedNodes = new Map>(); + private readonly nodes = new Map>(); private readonly sorter?: ITreeSorter; private readonly subTreeRefreshPromises = new Map, Promise>(); @@ -326,7 +326,7 @@ export class AsyncDataTree implements IDisposable }; } - this.renderedNodes.set(null, this.root); + this.nodes.set(null, this.root); this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables); } @@ -413,7 +413,7 @@ export class AsyncDataTree implements IDisposable } hasNode(element: T): boolean { - return this.renderedNodes.has(element); + return this.nodes.has(element); } // View @@ -574,7 +574,7 @@ export class AsyncDataTree implements IDisposable // Implementation private getDataNode(element: TInput | T): IAsyncDataTreeNode { - const node: IAsyncDataTreeNode | undefined = this.renderedNodes.get((element === this.root.element ? null : element) as T); + const node: IAsyncDataTreeNode | undefined = this.nodes.get((element === this.root.element ? null : element) as T); if (!node) { throw new Error(`Data tree node not found: ${element}`); @@ -809,7 +809,7 @@ export class AsyncDataTree implements IDisposable const onDidCreateNode = (treeNode: ITreeNode, TFilterData>) => { if (treeNode.element.element) { insertedElements.add(treeNode.element.element as T); - this.renderedNodes.set(treeNode.element.element as T, treeNode.element); + this.nodes.set(treeNode.element.element as T, treeNode.element); } }; @@ -817,7 +817,7 @@ export class AsyncDataTree implements IDisposable if (treeNode.element.element) { if (!insertedElements.has(treeNode.element.element as T)) { treeNode.element.disposed = true; - this.renderedNodes.delete(treeNode.element.element as T); + this.nodes.delete(treeNode.element.element as T); } } }; From f859153fd933ac5348913292b699729fc9a18876 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 13:49:30 +0100 Subject: [PATCH 0971/1066] Glob: * does not match empty string (#67835) --- src/vs/base/common/glob.ts | 14 +++++++------- src/vs/base/test/node/glob.test.ts | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/glob.ts b/src/vs/base/common/glob.ts index f9c74b9ed46..a26f954ef31 100644 --- a/src/vs/base/common/glob.ts +++ b/src/vs/base/common/glob.ts @@ -302,7 +302,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check const base = pattern.substr(4); // '**/*'.length === 4 parsedPattern = function (path, basename) { - return path && strings.endsWith(path, base) ? pattern : null; + return typeof path === 'string' && strings.endsWith(path, base) ? pattern : null; }; } else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check parsedPattern = trivia2(match[1], pattern); @@ -348,7 +348,7 @@ function trivia2(base: string, originalPattern: string): ParsedStringPattern { const slashBase = `/${base}`; const backslashBase = `\\${base}`; const parsedPattern: ParsedStringPattern = function (path, basename) { - if (!path) { + if (typeof path !== 'string') { return null; } if (basename) { @@ -399,9 +399,9 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par const nativePath = paths.nativeSep !== paths.sep ? path.replace(ALL_FORWARD_SLASHES, paths.nativeSep) : path; const nativePathEnd = paths.nativeSep + nativePath; const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) { - return path && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null; + return typeof path === 'string' && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null; } : function (path, basename) { - return path && path === nativePath ? pattern : null; + return typeof path === 'string' && path === nativePath ? pattern : null; }; parsedPattern.allPaths = [(matchPathEnds ? '*/' : './') + path]; return parsedPattern; @@ -412,7 +412,7 @@ function toRegExp(pattern: string): ParsedStringPattern { const regExp = new RegExp(`^${parseRegExp(pattern)}$`); return function (path: string, basename: string) { regExp.lastIndex = 0; // reset RegExp to its initial state to reuse it! - return path && regExp.test(path) ? pattern : null; + return typeof path === 'string' && regExp.test(path) ? pattern : null; }; } catch (error) { return NULL; @@ -430,7 +430,7 @@ function toRegExp(pattern: string): ParsedStringPattern { export function match(pattern: string | IRelativePattern, path: string): boolean; export function match(expression: IExpression, path: string, hasSibling?: (name: string) => boolean): string /* the matching pattern */; export function match(arg1: string | IExpression | IRelativePattern, path: string, hasSibling?: (name: string) => boolean): any { - if (!arg1 || !path) { + if (!arg1 || typeof path !== 'string') { return false; } @@ -675,7 +675,7 @@ function aggregateBasenameMatches(parsedPatterns: Array[]); } const aggregate: ParsedStringPattern = function (path, basename) { - if (!path) { + if (typeof path !== 'string') { return null; } if (!basename) { diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index 7b003cc002b..ee10d64b839 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -102,6 +102,9 @@ suite('Glob', () => { p = 'C:/DNXConsoleApp/**/*.cs'; assertGlobMatch(p, 'C:\\DNXConsoleApp\\Program.cs'); assertGlobMatch(p, 'C:\\DNXConsoleApp\\foo\\Program.cs'); + + p = '*'; + assertGlobMatch(p, ''); }); test('dot hidden', function () { From d2ec27494b0abc0bf1123183e3a72a11d4d93982 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 4 Feb 2019 14:34:14 +0100 Subject: [PATCH 0972/1066] update references view --- build/builtInExtensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json index 1ce94db95eb..d0d0107677b 100644 --- a/build/builtInExtensions.json +++ b/build/builtInExtensions.json @@ -31,7 +31,7 @@ }, { "name": "ms-vscode.references-view", - "version": "0.0.24", + "version": "0.0.25", "repo": "https://github.com/Microsoft/vscode-reference-view", "metadata": { "id": "dc489f46-520d-4556-ae85-1f9eab3c412d", From a592b43925bed0dbf5ad1c522498b50ac41c89f3 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 12:15:38 +0100 Subject: [PATCH 0973/1066] Debt: Remove unused gulp tasks --- build/gulpfile.extensions.js | 2 -- gulpfile.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 1488f96ec44..c7591abc6c6 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -163,10 +163,8 @@ const tasks = compilations.map(function (tsconfigFile) { }; }); -gulp.task('clean-extensions', tasks.map(t => t.clean)); gulp.task('compile-extensions', tasks.map(t => t.compile)); gulp.task('watch-extensions', tasks.map(t => t.watch)); -gulp.task('clean-extensions-build', tasks.map(t => t.cleanBuild)); gulp.task('compile-extensions-build', tasks.map(t => t.compileBuild)); gulp.task('watch-extensions-build', tasks.map(t => t.watchBuild)); diff --git a/gulpfile.js b/gulpfile.js index fe899377d79..133717f6c3a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -27,12 +27,10 @@ gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('o gulp.task('default', ['compile']); // All -gulp.task('clean', ['clean-client', 'clean-extensions']); gulp.task('compile', ['monaco-typecheck', 'compile-client', 'compile-extensions']); gulp.task('watch', [/* 'monaco-typecheck-watch', */ 'watch-client', 'watch-extensions']); // All Build -gulp.task('clean-build', ['clean-client-build', 'clean-extensions-build']); gulp.task('compile-build', ['compile-client-build', 'compile-extensions-build']); gulp.task('watch-build', ['watch-client-build', 'watch-extensions-build']); From 77b61a1e552aa6dd00ec9d9bf6f6e980665fa117 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 14:41:29 +0100 Subject: [PATCH 0974/1066] Simplify gulp tasks for extensions --- build/gulpfile.extensions.js | 44 ++++++++++++------------------------ build/lib/util.js | 33 +++++++++++++++++++++++++++ build/lib/util.ts | 34 ++++++++++++++++++++++++++++ gulpfile.js | 2 -- 4 files changed, 81 insertions(+), 32 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index c7591abc6c6..1d0ac3da216 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -47,11 +47,7 @@ const tasks = compilations.map(function (tsconfigFile) { const clean = 'clean-extension:' + name; const compile = 'compile-extension:' + name; const watch = 'watch-extension:' + name; - - // Build Tasks - const cleanBuild = 'clean-extension-build:' + name; const compileBuild = 'compile-extension-build:' + name; - const watchBuild = 'watch-extension-build:' + name; const root = path.join('extensions', relativeDirname); const srcBase = path.join(root, 'src'); @@ -112,15 +108,18 @@ const tasks = compilations.map(function (tsconfigFile) { const srcOpts = { cwd: path.dirname(__dirname), base: srcBase }; gulp.task(clean, cb => rimraf(out, cb)); + const cleanTask = () => util.primraf(out); - gulp.task(compile, [clean], () => { + gulp.task(compile, util.taskSeries(cleanTask, () => { const pipeline = createPipeline(false, true); const input = gulp.src(src, srcOpts); - return input + return util.streamToPromise( + input .pipe(pipeline()) - .pipe(gulp.dest(out)); - }); + .pipe(gulp.dest(out)) + ); + })); gulp.task(watch, [clean], () => { const pipeline = createPipeline(false); @@ -132,39 +131,24 @@ const tasks = compilations.map(function (tsconfigFile) { .pipe(gulp.dest(out)); }); - gulp.task(cleanBuild, cb => rimraf(out, cb)); - - gulp.task(compileBuild, [clean], () => { + gulp.task(compileBuild, util.taskSeries(cleanTask, () => { const pipeline = createPipeline(true, true); const input = gulp.src(src, srcOpts); - return input + return util.streamToPromise( + input .pipe(pipeline()) - .pipe(gulp.dest(out)); - }); - - gulp.task(watchBuild, [clean], () => { - const pipeline = createPipeline(true); - const input = gulp.src(src, srcOpts); - const watchInput = watcher(src, srcOpts); - - return watchInput - .pipe(util.incremental(() => pipeline(), input)) - .pipe(gulp.dest(out)); - }); + .pipe(gulp.dest(out)) + ); + })); return { - clean: clean, compile: compile, watch: watch, - cleanBuild: cleanBuild, - compileBuild: compileBuild, - watchBuild: watchBuild + compileBuild: compileBuild }; }); gulp.task('compile-extensions', tasks.map(t => t.compile)); gulp.task('watch-extensions', tasks.map(t => t.watch)); - gulp.task('compile-extensions-build', tasks.map(t => t.compileBuild)); -gulp.task('watch-extensions-build', tasks.map(t => t.watchBuild)); diff --git a/build/lib/util.js b/build/lib/util.js index e6f041f3d60..a03adac0f37 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -183,6 +183,39 @@ function rimraf(dir) { return cb => retry(cb); } exports.rimraf = rimraf; +/** + * Like rimraf (with 5 retries), but with a promise instead of a callback. + */ +function primraf(dir) { + const fn = rimraf(dir); + return new Promise((resolve, reject) => { + fn((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + }); +} +exports.primraf = primraf; +/** + * Convert a stream to a promise. + */ +function streamToPromise(stream) { + return new Promise((resolve, reject) => { + stream.on('end', _ => resolve()); + stream.on('error', err => reject(err)); + }); +} +exports.streamToPromise = streamToPromise; +function taskSeries(...tasks) { + return async () => { + for (let i = 0; i < tasks.length; i++) { + await tasks[i](); + } + }; +} +exports.taskSeries = taskSeries; function getVersion(root) { let version = process.env['BUILD_SOURCEVERSION']; if (!version || !/^[0-9a-f]{40}$/i.test(version)) { diff --git a/build/lib/util.ts b/build/lib/util.ts index 4617cc83f2f..918c2295b3b 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -237,6 +237,40 @@ export function rimraf(dir: string): (cb: any) => void { return cb => retry(cb); } +/** + * Like rimraf (with 5 retries), but with a promise instead of a callback. + */ +export function primraf(dir: string): Promise { + const fn = rimraf(dir); + return new Promise((resolve, reject) => { + fn((err: any) => { + if (err) { + return reject(err); + } + resolve(); + }); + }); +} + +/** + * Convert a stream to a promise. + */ +export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { + return new Promise((resolve, reject) => { + stream.on('end', _ => resolve()); + stream.on('error', err => reject(err)); + }); +} + +export type PromiseTask = () => Promise; +export function taskSeries(...tasks: PromiseTask[]): () => Promise { + return async () => { + for (let i = 0; i < tasks.length; i++) { + await tasks[i](); + } + }; +} + export function getVersion(root: string): string | undefined { let version = process.env['BUILD_SOURCEVERSION']; diff --git a/gulpfile.js b/gulpfile.js index 133717f6c3a..882d7f84147 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -21,7 +21,6 @@ gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)) // Full compile, including nls and inline sources in sourcemaps, for build gulp.task('clean-client-build', util.rimraf('out-build')); gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('src', 'out-build', true)); -gulp.task('watch-client-build', ['clean-client-build'], compilation.watchTask('out-build', true)); // Default gulp.task('default', ['compile']); @@ -32,7 +31,6 @@ gulp.task('watch', [/* 'monaco-typecheck-watch', */ 'watch-client', 'watch-exten // All Build gulp.task('compile-build', ['compile-client-build', 'compile-extensions-build']); -gulp.task('watch-build', ['watch-client-build', 'watch-extensions-build']); process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); From 11f724465324425150120373bafed33590cf7a52 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 4 Feb 2019 15:14:48 +0100 Subject: [PATCH 0975/1066] strict null streams, #67775 --- src/tsconfig.strictNullChecks.json | 3 ++- .../files/electron-browser/streams.ts | 24 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 19d2a4737a0..5d4073c2391 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -791,6 +791,7 @@ "./vs/workbench/services/extensions/node/rpcProtocol.ts", "./vs/workbench/services/extensions/test/node/rpcProtocol.test.ts", "./vs/workbench/services/files/electron-browser/encoding.ts", + "./vs/workbench/services/files/electron-browser/streams.ts", "./vs/workbench/services/files/node/watcher/common.ts", "./vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts", "./vs/workbench/services/files/node/watcher/nsfw/test/nsfwWatcherService.test.ts", @@ -891,4 +892,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/files/electron-browser/streams.ts b/src/vs/workbench/services/files/electron-browser/streams.ts index d4af4580a88..f3a5a3fe034 100644 --- a/src/vs/workbench/services/files/electron-browser/streams.ts +++ b/src/vs/workbench/services/files/electron-browser/streams.ts @@ -31,7 +31,7 @@ function createSimpleWritable(provider: IFileSystemProvider, resource: URI, opts } end() { // todo@joh - end might have another chunk... - provider.writeFile(resource, Buffer.concat(this._chunks), opts).then(_ => { + provider.writeFile!(resource, Buffer.concat(this._chunks), opts).then(_ => { super.end(); }, err => { this.emit('error', err); @@ -50,9 +50,9 @@ 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, { create: true }); + this._fd = await provider.open!(resource, { create: true }); } - let bytesWritten = await provider.write(this._fd, this._pos, chunk, 0, chunk.length); + let bytesWritten = await provider.write!(this._fd, this._pos, chunk, 0, chunk.length); this._pos += bytesWritten; callback(); } catch (err) { @@ -61,9 +61,9 @@ function createWritable(provider: IFileSystemProvider, resource: URI, opts: File } _final(callback: (err?: any) => any) { if (typeof this._fd !== 'number') { - provider.open(resource, { create: true }).then(fd => provider.close(fd)).finally(callback); + provider.open!(resource, { create: true }).then(fd => provider.close!(fd)).finally(callback); } else { - provider.close(this._fd).finally(callback); + provider.close!(this._fd).finally(callback); } } }; @@ -85,20 +85,20 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: _pos: number = position; _reading: boolean = false; - async _read(size?: number) { + async _read(size: number = 2 ** 10) { if (this._reading) { return; } this._reading = true; try { if (typeof this._fd !== 'number') { - this._fd = await provider.open(resource, { create: false }); + this._fd = await provider.open!(resource, { create: false }); } while (this._reading) { let buffer = Buffer.allocUnsafe(size); - let bytesRead = await provider.read(this._fd, this._pos, buffer, 0, buffer.length); + let bytesRead = await provider.read!(this._fd, this._pos, buffer, 0, buffer.length); if (bytesRead === 0) { - await provider.close(this._fd); + await provider.close!(this._fd); this._reading = false; this.push(null); } else { @@ -113,7 +113,7 @@ function createReadable(provider: IFileSystemProvider, resource: URI, position: } _destroy(_err: any, callback: (err?: any) => any) { if (typeof this._fd === 'number') { - provider.close(this._fd).then(callback, callback); + provider.close!(this._fd).then(callback, callback); } } }; @@ -126,7 +126,7 @@ function createSimpleReadable(provider: IFileSystemProvider, resource: URI, posi if (this._readOperation) { return; } - this._readOperation = provider.readFile(resource).then(data => { + this._readOperation = provider.readFile!(resource).then(data => { this.push(data.slice(position)); this.push(null); }, err => { @@ -141,7 +141,7 @@ export function createReadableOfSnapshot(snapshot: ITextSnapshot): Readable { return new Readable({ read: function () { try { - let chunk: string; + let chunk: string | null = null; let canPush = true; // Push all chunks as long as we can push and as long as From 5c7748690dc7d789a0992c219560b45f5fcf3f5a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 4 Feb 2019 15:17:10 +0100 Subject: [PATCH 0976/1066] always wait when starting with `prof-append-timers` --- .../parts/performance/electron-browser/startupTimings.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts index 949e0d28051..c1a0b9a2a42 100644 --- a/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/parts/performance/electron-browser/startupTimings.ts @@ -65,18 +65,11 @@ export class StartupTimings implements IWorkbenchContribution { return; } - const waitWhenNoCachedData: () => Promise = () => { - // wait 15s for cached data to be produced - return !didUseCachedData() - ? timeout(15000) - : Promise.resolve(); - }; - const { sessionId } = await this._telemetryService.getTelemetryInfo(); Promise.all([ this._timerService.startupMetrics, - waitWhenNoCachedData(), + timeout(15000), // wait: cached data creation, telemetry sending ]).then(([startupMetrics]) => { return nfcall(appendFile, appendTo, `${startupMetrics.ellapsed}\t${product.nameShort}\t${(product.commit || '').slice(0, 10) || '0000000000'}\t${sessionId}\t${isStandardStartup ? 'standard_start' : 'NO_standard_start'}\n`); }).then(() => { From 8299260c6270a7b2325e20e50e8ee0de9a890277 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 4 Feb 2019 15:18:47 +0100 Subject: [PATCH 0977/1066] Strict null check 'quickOpenWidget.ts' (fixes #67774) --- src/tsconfig.strictNullChecks.json | 1 + .../quickopen/browser/quickOpenViewer.ts | 6 ++-- .../quickopen/browser/quickOpenWidget.ts | 31 +++++++++++-------- .../base/parts/quickopen/common/quickOpen.ts | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 5d4073c2391..ef2000cae7e 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -38,6 +38,7 @@ "./vs/base/parts/quickopen/common/quickOpen.ts", "./vs/base/parts/quickopen/common/quickOpenScorer.ts", "./vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts", + "./vs/base/parts/quickopen/browser/quickOpenWidget.ts", "./vs/base/parts/tree/browser/tree.ts", "./vs/base/parts/tree/browser/treeDefaults.ts", "./vs/base/parts/tree/browser/treeDnd.ts", diff --git a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts index 042d649f924..a6c8e390574 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenViewer.ts @@ -24,7 +24,7 @@ export class DataSource implements IDataSource { getId(tree: ITree, element: any): string { if (!element) { - return null; + return null!; } const model = this.modelProvider.getModel(); @@ -49,10 +49,10 @@ export class DataSource implements IDataSource { export class AccessibilityProvider implements IAccessibilityProvider { constructor(private modelProvider: IModelProvider) { } - getAriaLabel(tree: ITree, element: any): string { + getAriaLabel(tree: ITree, element: any): string | null { const model = this.modelProvider.getModel(); - return model.accessibilityProvider && model.accessibilityProvider.getAriaLabel(element); + return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null; } getPosInSet(tree: ITree, element: any): string { diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index 65cdc14af54..c121e1d3b58 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -110,12 +110,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { private visible: boolean; private isLoosingFocus: boolean; private callbacks: IQuickOpenCallbacks; - private quickNavigateConfiguration: IQuickNavigateConfiguration; + private quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; private container: HTMLElement; private treeElement: HTMLElement; private inputElement: HTMLElement; private layoutDimensions: DOM.Dimension; - private model: IModel; + private model: IModel | null; private inputChangingTimeoutHandle: any; private styles: IQuickOpenStyles; private renderer: Renderer; @@ -137,7 +137,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } getModel(): IModel { - return this.model; + return this.model!; } setCallbacks(callbacks: IQuickOpenCallbacks): void { @@ -181,7 +181,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { DOM.addClass(this.inputContainer, 'quick-open-input'); this.element.appendChild(this.inputContainer); - this.inputBox = this._register(new InputBox(this.inputContainer, null, { + this.inputBox = this._register(new InputBox(this.inputContainer, undefined, { placeholder: this.options.inputPlaceHolder || '', ariaLabel: DEFAULT_INPUT_ARIA_LABEL, inputBackground: this.styles.inputBackground, @@ -538,10 +538,15 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } // ARIA - this.inputElement.setAttribute('aria-activedescendant', this.treeElement.getAttribute('aria-activedescendant')); + const arivaActiveDescendant = this.treeElement.getAttribute('aria-activedescendant'); + if (arivaActiveDescendant) { + this.inputElement.setAttribute('aria-activedescendant', arivaActiveDescendant); + } else { + this.inputElement.removeAttribute('aria-activedescendant'); + } const context: IEntryRunContext = { event: event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - this.model.runner.run(value, Mode.PREVIEW, context); + this.model!.runner.run(value, Mode.PREVIEW, context); } private elementSelected(value: any, event?: any, preferredMode?: Mode): void { @@ -553,7 +558,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { const context: IEntryRunContext = { event, keymods: this.extractKeyMods(event), quickNavigateConfiguration: this.quickNavigateConfiguration }; - hide = this.model.runner.run(value, mode, context); + hide = this.model!.runner.run(value, mode, context); } // Hide if command was run successfully @@ -603,7 +608,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { if (types.isString(param)) { this.doShowWithPrefix(param); } else { - if (options.value) { + if (options && options.value) { this.restoreLastInput(options.value); } this.doShowWithInput(param, options && options.autoFocus ? options.autoFocus : {}); @@ -747,13 +752,13 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { // Indicate entries to tree this.tree.layout(); - const entries = input ? input.entries.filter(e => this.isElementVisible(input, e)) : []; + const entries = input ? input.entries!.filter(e => this.isElementVisible(input!, e)) : []; this.updateResultCount(entries.length); // Handle auto focus if (autoFocus) { if (entries.length) { - this.autoFocus(input, entries, autoFocus); + this.autoFocus(input!, entries, autoFocus); } } }); @@ -770,7 +775,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { let height = 0; - let preferredItemsHeight: number; + let preferredItemsHeight: number | undefined; if (this.layoutDimensions && this.layoutDimensions.height) { preferredItemsHeight = (this.layoutDimensions.height - 50 /* subtract height of input field (30px) and some spacing (drop shadow) to fit */) * 0.4 /* max 40% of screen */; } @@ -834,12 +839,12 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { } if (this.callbacks.onHide) { - this.callbacks.onHide(reason); + this.callbacks.onHide(reason!); } } getQuickNavigateConfiguration(): IQuickNavigateConfiguration { - return this.quickNavigateConfiguration; + return this.quickNavigateConfiguration!; } setPlaceHolder(placeHolder: string): void { diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts index e6d87a0b0e7..a7b46b23c46 100644 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ b/src/vs/base/parts/quickopen/common/quickOpen.ts @@ -49,7 +49,7 @@ export const enum Mode { export interface IEntryRunContext { event: any; keymods: IKeyMods; - quickNavigateConfiguration: IQuickNavigateConfiguration; + quickNavigateConfiguration: IQuickNavigateConfiguration | undefined; } export interface IKeyMods { From bd8486b1c5fe83509bb8bf7fc4ceeb1981193748 Mon Sep 17 00:00:00 2001 From: Andre Weinand Date: Mon, 4 Feb 2019 15:38:34 +0100 Subject: [PATCH 0978/1066] properly initialise rendering data for multi-thread case; fixes #67791 --- .../parts/debug/electron-browser/callStackView.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts index 91e17036d60..4be39474958 100644 --- a/src/vs/workbench/parts/debug/electron-browser/callStackView.ts +++ b/src/vs/workbench/parts/debug/electron-browser/callStackView.ts @@ -398,13 +398,13 @@ class ThreadsRenderer implements ITreeRenderer, index: number, data: IThreadTemplateData): void { @@ -483,7 +483,7 @@ class ErrorsRenderer implements ITreeRenderer Date: Mon, 4 Feb 2019 15:43:57 +0100 Subject: [PATCH 0979/1066] Extensions view broken (fixes #67843) --- src/vs/platform/list/browser/listService.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index d49a2602d96..0966123e57d 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -359,17 +359,18 @@ export class WorkbenchPagedList extends PagedList { @IKeybindingService keybindingService: IKeybindingService ) { const horizontalScrolling = typeof options.horizontalScrolling !== 'undefined' ? options.horizontalScrolling : configurationService.getValue(horizontalScrollingKey); + const [workbenchListOptions, workbenchListOptionsDisposable] = toWorkbenchListOptions(options, configurationService, keybindingService); super(container, delegate, renderers, { keyboardSupport: false, styleController: new DefaultStyleController(getSharedListStyleSheet()), ...computeStyles(themeService.getTheme(), defaultListStyles), - ...toWorkbenchListOptions(options, configurationService, keybindingService), + ...workbenchListOptions, horizontalScrolling } as IListOptions ); - this.disposables = []; + this.disposables = [workbenchListOptionsDisposable]; this.contextKeyService = createScopedContextKeyService(contextKeyService, this); this.configurationService = configurationService; From b46c77b7ad2048edf14db1d4e83678c2119af67a Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Mon, 4 Feb 2019 15:45:08 +0100 Subject: [PATCH 0980/1066] Treat Composer lock files as JSON by default. Fixes #67859. --- extensions/json/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/json/package.json b/extensions/json/package.json index 517346cd347..0ee9501609f 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -29,6 +29,7 @@ ".css.map" ], "filenames": [ + "composer.lock", ".watchmanconfig", ".ember-cli" ], From 9af78e6fd8472a8006552ccd83551692f1b6ed1c Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Mon, 4 Feb 2019 15:58:17 +0100 Subject: [PATCH 0981/1066] fixes #67860 --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 3890deafa57..98c194d5e6d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -412,8 +412,8 @@ export class AsyncDataTree implements IDisposable await this.refreshAndRenderNode(this.getDataNode(element), recursive, ChildrenResolutionReason.Refresh, viewStateContext); } - hasNode(element: T): boolean { - return this.nodes.has(element); + hasNode(element: TInput | T): boolean { + return element === this.root.element || this.nodes.has(element as T); } // View From 5830f6857c9215dd82739bcae377aecf7cac6d05 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 4 Feb 2019 16:05:18 +0100 Subject: [PATCH 0982/1066] auto select theme under development (fixes #62582) --- .../themes/electron-browser/colorThemeStore.ts | 14 ++++++++++++-- .../electron-browser/fileIconThemeStore.ts | 9 +++++++++ .../electron-browser/workbenchThemeService.ts | 16 ++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 0b724501115..20732b19d53 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -8,7 +8,7 @@ import * as nls from 'vs/nls'; import * as types from 'vs/base/common/types'; import * as resources from 'vs/base/common/resources'; import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IColorTheme, ExtensionData, IThemeExtensionPoint, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { ExtensionData, IThemeExtensionPoint, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ColorThemeData } from 'vs/workbench/services/themes/electron-browser/colorThemeData'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Event, Emitter } from 'vs/base/common/event'; @@ -155,7 +155,17 @@ export class ColorThemeStore { }); } - public getColorThemes(): Promise { + public findThemeDataByParentLocation(parentLocation: URI): Promise { + if (parentLocation) { + return this.getColorThemes().then(allThemes => { + return allThemes.filter(t => t.location && resources.isEqualOrParent(t.location, parentLocation)); + }); + } + return Promise.resolve([]); + + } + + public getColorThemes(): Promise { return this.extensionService.whenInstalledExtensionsRegistered().then(_ => { return this.extensionsColorThemes; }); diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 437387c81f4..147c0361aad 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -154,6 +154,15 @@ export class FileIconThemeStore { }); } + public findThemeDataByParentLocation(parentLocation: URI): any { + if (parentLocation) { + return this.getFileIconThemes().then(allThemes => { + return allThemes.filter(t => t.location && resources.isEqualOrParent(t.location, parentLocation)); + }); + } + return Promise.resolve([]); + } + public getFileIconThemes(): Promise { return this.extensionService.whenInstalledExtensionsRegistered().then(isReady => { return this.knownIconThemes; diff --git a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts index e0d5b16bf91..8413a65817a 100644 --- a/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/electron-browser/workbenchThemeService.ts @@ -257,10 +257,22 @@ export class WorkbenchThemeService implements IWorkbenchThemeService { return Promise.all([ this.colorThemeStore.findThemeDataBySettingsId(colorThemeSetting, DEFAULT_THEME_ID).then(theme => { - return this.setColorTheme(theme && theme.id, undefined); + return this.colorThemeStore.findThemeDataByParentLocation(this.environmentService.extensionDevelopmentLocationURI).then(devThemes => { + if (devThemes.length) { + return this.setColorTheme(devThemes[0].id, ConfigurationTarget.MEMORY); + } else { + return this.setColorTheme(theme && theme.id, undefined); + } + }); }), this.iconThemeStore.findThemeBySettingsId(iconThemeSetting).then(theme => { - return this.setFileIconTheme(theme && theme.id, undefined); + return this.iconThemeStore.findThemeDataByParentLocation(this.environmentService.extensionDevelopmentLocationURI).then(devThemes => { + if (devThemes.length) { + return this.setFileIconTheme(devThemes[0].id, ConfigurationTarget.MEMORY); + } else { + return this.setFileIconTheme(theme && theme.id, undefined); + } + }); }), ]); } From 0280160bd2176ca40e26878e30c48bf0a4695830 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 16:04:15 +0100 Subject: [PATCH 0983/1066] More simplifications to extensions gulp tasks (#67863) --- build/gulpfile.extensions.js | 41 ++++++++++++++++++------------------ build/lib/util.js | 25 +++++++++++++++------- build/lib/util.ts | 21 ++++++++++++------ 3 files changed, 52 insertions(+), 35 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 1d0ac3da216..d4b8ed03796 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -11,7 +11,6 @@ const path = require('path'); const tsb = require('gulp-tsb'); const es = require('event-stream'); const filter = require('gulp-filter'); -const rimraf = require('rimraf'); const util = require('./lib/util'); const watcher = require('./lib/watch'); const createReporter = require('./lib/reporter').createReporter; @@ -43,12 +42,6 @@ const tasks = compilations.map(function (tsconfigFile) { const name = relativeDirname.replace(/\//g, '-'); - // Tasks - const clean = 'clean-extension:' + name; - const compile = 'compile-extension:' + name; - const watch = 'watch-extension:' + name; - const compileBuild = 'compile-extension-build:' + name; - const root = path.join('extensions', relativeDirname); const srcBase = path.join(root, 'src'); const src = path.join(srcBase, '**'); @@ -107,10 +100,9 @@ const tasks = compilations.map(function (tsconfigFile) { const srcOpts = { cwd: path.dirname(__dirname), base: srcBase }; - gulp.task(clean, cb => rimraf(out, cb)); const cleanTask = () => util.primraf(out); - gulp.task(compile, util.taskSeries(cleanTask, () => { + const compileTask = util.task.series(cleanTask, () => { const pipeline = createPipeline(false, true); const input = gulp.src(src, srcOpts); @@ -119,19 +111,21 @@ const tasks = compilations.map(function (tsconfigFile) { .pipe(pipeline()) .pipe(gulp.dest(out)) ); - })); + }); - gulp.task(watch, [clean], () => { + const watchTask = util.task.series(cleanTask, () => { const pipeline = createPipeline(false); const input = gulp.src(src, srcOpts); const watchInput = watcher(src, srcOpts); - return watchInput + return util.streamToPromise( + watchInput .pipe(util.incremental(pipeline, input)) - .pipe(gulp.dest(out)); + .pipe(gulp.dest(out)) + ); }); - gulp.task(compileBuild, util.taskSeries(cleanTask, () => { + const compileBuildTask = util.task.series(cleanTask, () => { const pipeline = createPipeline(true, true); const input = gulp.src(src, srcOpts); @@ -140,15 +134,20 @@ const tasks = compilations.map(function (tsconfigFile) { .pipe(pipeline()) .pipe(gulp.dest(out)) ); - })); + }); + + // Tasks + gulp.task('compile-extension:' + name, compileTask); + gulp.task('watch-extension:' + name, watchTask); + gulp.task('compile-extension-build:' + name, compileBuildTask); return { - compile: compile, - watch: watch, - compileBuild: compileBuild + compileTask: compileTask, + watchTask: watchTask, + compileBuildTask: compileBuildTask }; }); -gulp.task('compile-extensions', tasks.map(t => t.compile)); -gulp.task('watch-extensions', tasks.map(t => t.watch)); -gulp.task('compile-extensions-build', tasks.map(t => t.compileBuild)); +gulp.task('compile-extensions', util.task.parallel(...tasks.map(t => t.compileTask))); +gulp.task('watch-extensions', util.task.parallel(...tasks.map(t => t.watchTask))); +gulp.task('compile-extensions-build', util.task.parallel(...tasks.map(t => t.compileBuildTask))); diff --git a/build/lib/util.js b/build/lib/util.js index a03adac0f37..fdb0f815e75 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -208,14 +208,23 @@ function streamToPromise(stream) { }); } exports.streamToPromise = streamToPromise; -function taskSeries(...tasks) { - return async () => { - for (let i = 0; i < tasks.length; i++) { - await tasks[i](); - } - }; -} -exports.taskSeries = taskSeries; +var task; +(function (task) { + function series(...tasks) { + return async () => { + for (let i = 0; i < tasks.length; i++) { + await tasks[i](); + } + }; + } + task.series = series; + function parallel(...tasks) { + return async () => { + await Promise.all(tasks.map(t => t())); + }; + } + task.parallel = parallel; +})(task = exports.task || (exports.task = {})); function getVersion(root) { let version = process.env['BUILD_SOURCEVERSION']; if (!version || !/^[0-9a-f]{40}$/i.test(version)) { diff --git a/build/lib/util.ts b/build/lib/util.ts index 918c2295b3b..b831c3e8ed2 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -263,12 +263,21 @@ export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { } export type PromiseTask = () => Promise; -export function taskSeries(...tasks: PromiseTask[]): () => Promise { - return async () => { - for (let i = 0; i < tasks.length; i++) { - await tasks[i](); - } - }; + +export namespace task { + export function series(...tasks: PromiseTask[]): () => Promise { + return async () => { + for (let i = 0; i < tasks.length; i++) { + await tasks[i](); + } + }; + } + + export function parallel(...tasks: PromiseTask[]): () => Promise { + return async () => { + await Promise.all(tasks.map(t => t())); + }; + } } export function getVersion(root: string): string | undefined { From 8f95df52e132f47b20789d25d07dd3beffaef833 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 16:07:29 +0100 Subject: [PATCH 0984/1066] More simplifications (#67863) --- build/gulpfile.extensions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index d4b8ed03796..150fe7723c0 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -139,7 +139,6 @@ const tasks = compilations.map(function (tsconfigFile) { // Tasks gulp.task('compile-extension:' + name, compileTask); gulp.task('watch-extension:' + name, watchTask); - gulp.task('compile-extension-build:' + name, compileBuildTask); return { compileTask: compileTask, From 6e968190dfa181ce897b853410e1490bb5b03919 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 4 Feb 2019 16:35:48 +0100 Subject: [PATCH 0985/1066] Update grammars --- extensions/bat/cgmanifest.json | 4 +- .../bat/syntaxes/batchfile.tmLanguage.json | 4 +- extensions/cpp/cgmanifest.json | 4 +- extensions/docker/cgmanifest.json | 2 +- extensions/fsharp/cgmanifest.json | 4 +- extensions/git/cgmanifest.json | 2 +- extensions/groovy/cgmanifest.json | 2 +- extensions/hlsl/cgmanifest.json | 2 +- extensions/html/cgmanifest.json | 2 +- extensions/html/syntaxes/html.tmLanguage.json | 56 ++++++++++++++++++- extensions/ini/cgmanifest.json | 2 +- extensions/java/cgmanifest.json | 2 +- extensions/java/syntaxes/java.tmLanguage.json | 41 +++++++++++++- .../syntaxes/JavaScript.tmLanguage.json | 23 +++++++- .../syntaxes/JavaScriptReact.tmLanguage.json | 23 +++++++- extensions/json/cgmanifest.json | 2 +- extensions/lua/cgmanifest.json | 2 +- extensions/perl/cgmanifest.json | 2 +- extensions/php/cgmanifest.json | 4 +- extensions/php/syntaxes/php.tmLanguage.json | 4 +- extensions/pug/cgmanifest.json | 2 +- extensions/ruby/cgmanifest.json | 2 +- extensions/shaderlab/cgmanifest.json | 2 +- extensions/typescript-basics/cgmanifest.json | 2 +- .../syntaxes/TypeScript.tmLanguage.json | 23 +++++++- .../syntaxes/TypeScriptReact.tmLanguage.json | 23 +++++++- extensions/vb/cgmanifest.json | 2 +- extensions/yaml/cgmanifest.json | 2 +- 28 files changed, 205 insertions(+), 40 deletions(-) diff --git a/extensions/bat/cgmanifest.json b/extensions/bat/cgmanifest.json index 1e21679d7b2..5bc3e285f0c 100644 --- a/extensions/bat/cgmanifest.json +++ b/extensions/bat/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "mmims/language-batchfile", "repositoryUrl": "https://github.com/mmims/language-batchfile", - "commitHash": "4b67596631b4ecd2c89c2ec1b2e08a6623438903" + "commitHash": "95ea8c699f7a8296b15767069868532d52631c46" } }, "license": "MIT", - "version": "0.7.4" + "version": "0.7.5" } ], "version": 1 diff --git a/extensions/bat/syntaxes/batchfile.tmLanguage.json b/extensions/bat/syntaxes/batchfile.tmLanguage.json index 26ae88f43c5..9eff3c9de2b 100644 --- a/extensions/bat/syntaxes/batchfile.tmLanguage.json +++ b/extensions/bat/syntaxes/batchfile.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/mmims/language-batchfile/commit/4b67596631b4ecd2c89c2ec1b2e08a6623438903", + "version": "https://github.com/mmims/language-batchfile/commit/95ea8c699f7a8296b15767069868532d52631c46", "name": "Batch File", "scopeName": "source.batchfile", "patterns": [ @@ -46,7 +46,7 @@ "commands": { "patterns": [ { - "match": "(?<=^|[\\s@])(?i:adprep|append|arp|assoc|at|atmadm|attrib|auditpol|autochk|autoconv|autofmt|bcdboot|bcdedit|bdehdcfg|bitsadmin|bootcfg|brea|cacls|cd|certreq|certutil|change|chcp|chdir|chglogon|chgport|chgusr|chkdsk|chkntfs|choice|cipher|clip|cls|clscluadmin|cluster|cmd|cmdkey|cmstp|color|comp|compact|convert|copy|cprofile|cscript|csvde|date|dcdiag|dcgpofix|dcpromo|defra|del|dfscmd|dfsdiag|dfsrmig|diantz|dir|dirquota|diskcomp|diskcopy|diskpart|diskperf|diskraid|diskshadow|dispdiag|doin|dnscmd|doskey|driverquery|dsacls|dsadd|dsamain|dsdbutil|dsget|dsmgmt|dsmod|dsmove|dsquery|dsrm|edit|endlocal|eraseesentutl|eventcreate|eventquery|eventtriggers|evntcmd|expand|extract|fc|filescrn|find|findstr|finger|flattemp|fonde|forfiles|format|freedisk|fsutil|ftp|ftype|fveupdate|getmac|gettype|gpfixup|gpresult|gpupdate|graftabl|hashgen|hep|helpctr|hostname|icacls|iisreset|inuse|ipconfig|ipxroute|irftp|ismserv|jetpack|klist|ksetup|ktmutil|ktpass|label|ldifd|ldp|lodctr|logman|logoff|lpq|lpr|macfile|makecab|manage-bde|mapadmin|md|mkdir|mklink|mmc|mode|more|mount|mountvol|move|mqbup|mqsvc|mqtgsvc|msdt|msg|msiexec|msinfo32|mstsc|nbtstat|net computer|net group|net localgroup|net print|net session|net share|net start|net stop|net use|net user|net view|net|netcfg|netdiag|netdom|netsh|netstat|nfsadmin|nfsshare|nfsstat|nlb|nlbmgr|nltest|nslookup|ntackup|ntcmdprompt|ntdsutil|ntfrsutl|openfiles|pagefileconfig|path|pathping|pause|pbadmin|pentnt|perfmon|ping|pnpunatten|pnputil|popd|powercfg|powershell|powershell_ise|print|prncnfg|prndrvr|prnjobs|prnmngr|prnport|prnqctl|prompt|pubprn|pushd|pushprinterconnections|pwlauncher|qappsrv|qprocess|query|quser|qwinsta|rasdial|rcp|rd|rdpsign|regentc|recover|redircmp|redirusr|reg|regini|regsvr32|relog|ren|rename|rendom|repadmin|repair-bde|replace|reset session|rxec|risetup|rmdir|robocopy|route|rpcinfo|rpcping|rsh|runas|rundll32|rwinsta|sc|schtasks|scwcmd|secedit|serverceipoptin|servrmanagercmd|serverweroptin|setspn|setx|sfc|shadow|shift|showmount|shutdown|sort|start|storrept|subst|sxstrace|ysocmgr|systeminfo|takeown|tapicfg|taskkill|tasklist|tcmsetup|telnet|tftp|time|timeout|title|tlntadmn|tpmvscmgr|tpmvscmgr|tacerpt|tracert|tree|tscon|tsdiscon|tsecimp|tskill|tsprof|type|typeperf|tzutil|uddiconfig|umount|unlodctr|ver|verifier|verif|vol|vssadmin|w32tm|waitfor|wbadmin|wdsutil|wecutil|wevtutil|where|whoami|winnt|winnt32|winpop|winrm|winrs|winsat|wlbs|mic|wscript|xcopy)(?=$|\\s)", + "match": "(?<=^|[\\s@])(?i:adprep|append|arp|assoc|at|atmadm|attrib|auditpol|autochk|autoconv|autofmt|bcdboot|bcdedit|bdehdcfg|bitsadmin|bootcfg|brea|cacls|cd|certreq|certutil|change|chcp|chdir|chglogon|chgport|chgusr|chkdsk|chkntfs|choice|cipher|clip|cls|clscluadmin|cluster|cmd|cmdkey|cmstp|color|comp|compact|convert|copy|cprofile|cscript|csvde|date|dcdiag|dcgpofix|dcpromo|defra|del|dfscmd|dfsdiag|dfsrmig|diantz|dir|dirquota|diskcomp|diskcopy|diskpart|diskperf|diskraid|diskshadow|dispdiag|doin|dnscmd|doskey|driverquery|dsacls|dsadd|dsamain|dsdbutil|dsget|dsmgmt|dsmod|dsmove|dsquery|dsrm|edit|endlocal|eraseesentutl|eventcreate|eventquery|eventtriggers|evntcmd|expand|extract|fc|filescrn|find|findstr|finger|flattemp|fonde|forfiles|format|freedisk|fsutil|ftp|ftype|fveupdate|getmac|gettype|gpfixup|gpresult|gpupdate|graftabl|hashgen|hep|helpctr|hostname|icacls|iisreset|inuse|ipconfig|ipxroute|irftp|ismserv|jetpack|klist|ksetup|ktmutil|ktpass|label|ldifd|ldp|lodctr|logman|logoff|lpq|lpr|macfile|makecab|manage-bde|mapadmin|md|mkdir|mklink|mmc|mode|more|mount|mountvol|move|mqbup|mqsvc|mqtgsvc|msdt|msg|msiexec|msinfo32|mstsc|nbtstat|net computer|net group|net localgroup|net print|net session|net share|net start|net stop|net use|net user|net view|net|netcfg|netdiag|netdom|netsh|netstat|nfsadmin|nfsshare|nfsstat|nlb|nlbmgr|nltest|nslookup|ntackup|ntcmdprompt|ntdsutil|ntfrsutl|openfiles|pagefileconfig|path|pathping|pause|pbadmin|pentnt|perfmon|ping|pnpunatten|pnputil|popd|powercfg|powershell|powershell_ise|print|prncnfg|prndrvr|prnjobs|prnmngr|prnport|prnqctl|prompt|pubprn|pushd|pushprinterconnections|pwlauncher|qappsrv|qprocess|query|quser|qwinsta|rasdial|rcp|rd|rdpsign|regentc|recover|redircmp|redirusr|reg|regini|regsvr32|relog|ren|rename|rendom|repadmin|repair-bde|replace|reset session|rxec|risetup|rmdir|robocopy|route|rpcinfo|rpcping|rsh|runas|rundll32|rwinsta|scp|sc|schtasks|scwcmd|secedit|serverceipoptin|servrmanagercmd|serverweroptin|setspn|setx|sfc|shadow|shift|showmount|shutdown|sort|ssh|start|storrept|subst|sxstrace|ysocmgr|systeminfo|takeown|tapicfg|taskkill|tasklist|tcmsetup|telnet|tftp|time|timeout|title|tlntadmn|tpmvscmgr|tpmvscmgr|tacerpt|tracert|tree|tscon|tsdiscon|tsecimp|tskill|tsprof|type|typeperf|tzutil|uddiconfig|umount|unlodctr|ver|verifier|verif|vol|vssadmin|w32tm|waitfor|wbadmin|wdsutil|wecutil|wevtutil|where|whoami|winnt|winnt32|winpop|winrm|winrs|winsat|wlbs|mic|wscript|xcopy)(?=$|\\s)", "name": "keyword.command.batchfile" }, { diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index e6666933f61..7bab26bfa11 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-c", "repositoryUrl": "https://github.com/atom/language-c", - "commitHash": "9c0c5f202741a5647025db8d5df5fefba47b036c" + "commitHash": "3a269f88b12e512fb9495dc006a1dabf325d3d7f" } }, "license": "MIT", - "version": "0.58.1", + "version": "0.58.0", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/docker/cgmanifest.json b/extensions/docker/cgmanifest.json index 844d3120d3c..dbbf43607ee 100644 --- a/extensions/docker/cgmanifest.json +++ b/extensions/docker/cgmanifest.json @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index df594e070bb..851e97ca639 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "24c1588529af144d205f66fbcec6889500f9aaa9" + "commitHash": "be0bdfd1e272b6633f5edf1429052fe9fa4df7c1" } }, "license": "MIT", @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index a7e0b63aac3..8b62f6fbb0c 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -63,4 +63,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/groovy/cgmanifest.json b/extensions/groovy/cgmanifest.json index 07dec53a640..fc5a3ca9c12 100644 --- a/extensions/groovy/cgmanifest.json +++ b/extensions/groovy/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/hlsl/cgmanifest.json b/extensions/hlsl/cgmanifest.json index 92137372cc0..44b3a016ee0 100644 --- a/extensions/hlsl/cgmanifest.json +++ b/extensions/hlsl/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/html/cgmanifest.json b/extensions/html/cgmanifest.json index 7f3cc9396df..fb7007ddb31 100644 --- a/extensions/html/cgmanifest.json +++ b/extensions/html/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/html/syntaxes/html.tmLanguage.json b/extensions/html/syntaxes/html.tmLanguage.json index a5ccfed6f36..a071d90a9e3 100644 --- a/extensions/html/syntaxes/html.tmLanguage.json +++ b/extensions/html/syntaxes/html.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/textmate/html.tmbundle/commit/390c8870273a2ae80244dae6db6ba064a802f407", + "version": "https://github.com/textmate/html.tmbundle/commit/0c3d5ee54de3a993f747f54186b73a4d2d3c44a2", "name": "HTML", "scopeName": "text.html.basic", "injections": { @@ -222,7 +222,32 @@ ] } }, - "match": "[^\\n\"]+" + "match": "([^\\n\"/]|/(?![/*]))+" + }, + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.js" + } + }, + "end": "(?=\")|\\n", + "name": "comment.line.double-slash.js" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.js" + } + }, + "end": "(?=\")|\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.js" + } + }, + "name": "comment.block.js" } ] }, @@ -255,7 +280,32 @@ ] } }, - "match": "[^\\n']+" + "match": "([^\\n'/]|/(?![/*]))+" + }, + { + "begin": "//", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.js" + } + }, + "end": "(?=')|\\n", + "name": "comment.line.double-slash.js" + }, + { + "begin": "/\\*", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.begin.js" + } + }, + "end": "(?=')|\\*/", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.end.js" + } + }, + "name": "comment.block.js" } ] } diff --git a/extensions/ini/cgmanifest.json b/extensions/ini/cgmanifest.json index ef9fa28b58a..772dc25a3ac 100644 --- a/extensions/ini/cgmanifest.json +++ b/extensions/ini/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/java/cgmanifest.json b/extensions/java/cgmanifest.json index ba162b137ab..c32ed3e218f 100644 --- a/extensions/java/cgmanifest.json +++ b/extensions/java/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "atom/language-java", "repositoryUrl": "https://github.com/atom/language-java", - "commitHash": "7c3d0d3e37920790cd3afb4e68830bdb215851a3" + "commitHash": "f6d349a5ca698be25d3f48511888fe62d158e42c" } }, "license": "MIT", diff --git a/extensions/java/syntaxes/java.tmLanguage.json b/extensions/java/syntaxes/java.tmLanguage.json index 650a82c63be..777330ec4ae 100644 --- a/extensions/java/syntaxes/java.tmLanguage.json +++ b/extensions/java/syntaxes/java.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/atom/language-java/commit/7c3d0d3e37920790cd3afb4e68830bdb215851a3", + "version": "https://github.com/atom/language-java/commit/f6d349a5ca698be25d3f48511888fe62d158e42c", "name": "Java", "scopeName": "source.java", "patterns": [ @@ -640,6 +640,42 @@ }, "name": "meta.enum.java", "patterns": [ + { + "begin": "\\b(extends)\\b", + "beginCaptures": { + "1": { + "name": "storage.modifier.extends.java" + } + }, + "end": "(?={|\\bimplements\\b)", + "name": "meta.definition.class.inherited.classes.java", + "patterns": [ + { + "include": "#object-types-inherited" + }, + { + "include": "#comments" + } + ] + }, + { + "begin": "\\b(implements)\\b", + "beginCaptures": { + "1": { + "name": "storage.modifier.implements.java" + } + }, + "end": "(?={|\\bextends\\b)", + "name": "meta.definition.class.implemented.interfaces.java", + "patterns": [ + { + "include": "#object-types-inherited" + }, + { + "include": "#comments" + } + ] + }, { "begin": "{", "beginCaptures": { @@ -1468,6 +1504,9 @@ { "include": "#comments" }, + { + "include": "#storage-modifiers" + }, { "match": "\\|", "name": "punctuation.catch.separator.java" diff --git a/extensions/javascript/syntaxes/JavaScript.tmLanguage.json b/extensions/javascript/syntaxes/JavaScript.tmLanguage.json index 5d6df3a855e..fd84aa1a024 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/444971648e9e1e41c54c62f41d1310f2816965c4", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/a42e5cbe14945ccc0493f62b1e2d63eddcdaa6f6", "name": "JavaScript (with React support)", "scopeName": "source.js", "patterns": [ @@ -1581,7 +1581,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -3579,6 +3579,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.js", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.js" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { diff --git a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json b/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json index aaa5d8ceaba..3810070af9c 100644 --- a/extensions/javascript/syntaxes/JavaScriptReact.tmLanguage.json +++ b/extensions/javascript/syntaxes/JavaScriptReact.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/444971648e9e1e41c54c62f41d1310f2816965c4", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/a42e5cbe14945ccc0493f62b1e2d63eddcdaa6f6", "name": "JavaScript (with React support)", "scopeName": "source.js.jsx", "patterns": [ @@ -1581,7 +1581,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -3579,6 +3579,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.js.jsx", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.js.jsx" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { diff --git a/extensions/json/cgmanifest.json b/extensions/json/cgmanifest.json index d0b23b7e064..fabb7a93aba 100644 --- a/extensions/json/cgmanifest.json +++ b/extensions/json/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/lua/cgmanifest.json b/extensions/lua/cgmanifest.json index 78d8538bf08..6cefc1ca59e 100644 --- a/extensions/lua/cgmanifest.json +++ b/extensions/lua/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json index 6338921e75f..83d91107671 100644 --- a/extensions/perl/cgmanifest.json +++ b/extensions/perl/cgmanifest.json @@ -30,4 +30,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/php/cgmanifest.json b/extensions/php/cgmanifest.json index 3bbbd67fcaf..01cdb6b1761 100644 --- a/extensions/php/cgmanifest.json +++ b/extensions/php/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "language-php", "repositoryUrl": "https://github.com/atom/language-php", - "commitHash": "13842b6dfb4568f76e8fa0c08dbff74347ee13c6" + "commitHash": "b6c5e83016b52311cdc622c2579462861ee91587" } }, "license": "MIT", - "version": "0.43.2" + "version": "0.44.0" } ], "version": 1 diff --git a/extensions/php/syntaxes/php.tmLanguage.json b/extensions/php/syntaxes/php.tmLanguage.json index 62153a2b04b..bb4c134fbfc 100644 --- a/extensions/php/syntaxes/php.tmLanguage.json +++ b/extensions/php/syntaxes/php.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/atom/language-php/commit/13842b6dfb4568f76e8fa0c08dbff74347ee13c6", + "version": "https://github.com/atom/language-php/commit/b896ebfb6f669b8714f419527f047466420efe5c", "scopeName": "source.php", "patterns": [ { @@ -2696,7 +2696,7 @@ "name": "support.function.com.php" }, { - "begin": "(?i)\\b(isset|unset|eval|empty|list)\\b", + "match": "(?i)\\b(isset|unset|eval|empty|list)\\b", "name": "support.function.construct.php" }, { diff --git a/extensions/pug/cgmanifest.json b/extensions/pug/cgmanifest.json index cdfc2ded989..e7bd7bccc09 100644 --- a/extensions/pug/cgmanifest.json +++ b/extensions/pug/cgmanifest.json @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index dd4b29e5184..ffbbb7b71c6 100644 --- a/extensions/ruby/cgmanifest.json +++ b/extensions/ruby/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/shaderlab/cgmanifest.json b/extensions/shaderlab/cgmanifest.json index 3558286bc04..2dd540dc872 100644 --- a/extensions/shaderlab/cgmanifest.json +++ b/extensions/shaderlab/cgmanifest.json @@ -14,4 +14,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/typescript-basics/cgmanifest.json b/extensions/typescript-basics/cgmanifest.json index e604d6ec27b..2fbc739c7b5 100644 --- a/extensions/typescript-basics/cgmanifest.json +++ b/extensions/typescript-basics/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "TypeScript-TmLanguage", "repositoryUrl": "https://github.com/Microsoft/TypeScript-TmLanguage", - "commitHash": "444971648e9e1e41c54c62f41d1310f2816965c4" + "commitHash": "a42e5cbe14945ccc0493f62b1e2d63eddcdaa6f6" } }, "license": "MIT", diff --git a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json index 455f0756a72..b9dda5a5191 100644 --- a/extensions/typescript-basics/syntaxes/TypeScript.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScript.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/444971648e9e1e41c54c62f41d1310f2816965c4", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/a42e5cbe14945ccc0493f62b1e2d63eddcdaa6f6", "name": "TypeScript", "scopeName": "source.ts", "patterns": [ @@ -1578,7 +1578,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -3628,6 +3628,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.ts", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.ts" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { diff --git a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json b/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json index ded8400a283..76c725eccc8 100644 --- a/extensions/typescript-basics/syntaxes/TypeScriptReact.tmLanguage.json +++ b/extensions/typescript-basics/syntaxes/TypeScriptReact.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/444971648e9e1e41c54c62f41d1310f2816965c4", + "version": "https://github.com/Microsoft/TypeScript-TmLanguage/commit/a42e5cbe14945ccc0493f62b1e2d63eddcdaa6f6", "name": "TypeScriptReact", "scopeName": "source.tsx", "patterns": [ @@ -1581,7 +1581,7 @@ "include": "#parameter-name" }, { - "include": "#type-annotation" + "include": "#parameter-type-annotation" }, { "include": "#variable-initializer" @@ -3579,6 +3579,25 @@ } ] }, + "parameter-type-annotation": { + "patterns": [ + { + "name": "meta.type.annotation.tsx", + "begin": "(:)", + "beginCaptures": { + "1": { + "name": "keyword.operator.type.annotation.tsx" + } + }, + "end": "(?=[,)])|(?==[^>])", + "patterns": [ + { + "include": "#type" + } + ] + } + ] + }, "return-type": { "patterns": [ { diff --git a/extensions/vb/cgmanifest.json b/extensions/vb/cgmanifest.json index 4cb7d33e04b..23523f36abb 100644 --- a/extensions/vb/cgmanifest.json +++ b/extensions/vb/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index 80ba8ae522c..e6c3ca158b5 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -34,4 +34,4 @@ } ], "version": 1 -} +} \ No newline at end of file From e6d41395d9296eb68c985028ea4f7dff35e01574 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 4 Feb 2019 16:37:40 +0100 Subject: [PATCH 0986/1066] fix null checks --- .../services/themes/electron-browser/colorThemeStore.ts | 2 +- .../services/themes/electron-browser/fileIconThemeStore.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts index 20732b19d53..7955ba6b932 100644 --- a/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/colorThemeStore.ts @@ -155,7 +155,7 @@ export class ColorThemeStore { }); } - public findThemeDataByParentLocation(parentLocation: URI): Promise { + public findThemeDataByParentLocation(parentLocation: URI | undefined): Promise { if (parentLocation) { return this.getColorThemes().then(allThemes => { return allThemes.filter(t => t.location && resources.isEqualOrParent(t.location, parentLocation)); diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts index 147c0361aad..aed1e0de01d 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeStore.ts @@ -154,7 +154,7 @@ export class FileIconThemeStore { }); } - public findThemeDataByParentLocation(parentLocation: URI): any { + public findThemeDataByParentLocation(parentLocation: URI | undefined): any { if (parentLocation) { return this.getFileIconThemes().then(allThemes => { return allThemes.filter(t => t.location && resources.isEqualOrParent(t.location, parentLocation)); From 4676da4595f6fdeb5e388231dc8d726aad7556f5 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 16:57:39 +0100 Subject: [PATCH 0987/1066] More gulp clean task simiplifications (#67863) --- build/gulpfile.extensions.js | 18 ++++------- build/gulpfile.vscode.linux.js | 37 ++++++++--------------- build/gulpfile.vscode.win32.js | 4 +-- build/lib/util.js | 48 +++++++++++++++++++---------- build/lib/util.ts | 55 +++++++++++++++++++++++++--------- 5 files changed, 94 insertions(+), 68 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 150fe7723c0..64d5c213cc6 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -106,11 +106,9 @@ const tasks = compilations.map(function (tsconfigFile) { const pipeline = createPipeline(false, true); const input = gulp.src(src, srcOpts); - return util.streamToPromise( - input + return input .pipe(pipeline()) - .pipe(gulp.dest(out)) - ); + .pipe(gulp.dest(out)); }); const watchTask = util.task.series(cleanTask, () => { @@ -118,22 +116,18 @@ const tasks = compilations.map(function (tsconfigFile) { const input = gulp.src(src, srcOpts); const watchInput = watcher(src, srcOpts); - return util.streamToPromise( - watchInput + return watchInput .pipe(util.incremental(pipeline, input)) - .pipe(gulp.dest(out)) - ); + .pipe(gulp.dest(out)); }); const compileBuildTask = util.task.series(cleanTask, () => { const pipeline = createPipeline(true, true); const input = gulp.src(src, srcOpts); - return util.streamToPromise( - input + return input .pipe(pipeline()) - .pipe(gulp.dest(out)) - ); + .pipe(gulp.dest(out)); }); // Tasks diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index bddba8fe524..6728a0ce640 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -229,41 +229,28 @@ function buildSnapPackage(arch) { return shell.task(`cd ${snapBuildPath} && snapcraft build`); } -gulp.task('clean-vscode-linux-ia32-deb', util.rimraf('.build/linux/deb/i386')); -gulp.task('clean-vscode-linux-x64-deb', util.rimraf('.build/linux/deb/amd64')); -gulp.task('clean-vscode-linux-arm-deb', util.rimraf('.build/linux/deb/armhf')); -gulp.task('clean-vscode-linux-arm64-deb', util.rimraf('.build/linux/deb/arm64')); -gulp.task('clean-vscode-linux-ia32-rpm', util.rimraf('.build/linux/rpm/i386')); -gulp.task('clean-vscode-linux-x64-rpm', util.rimraf('.build/linux/rpm/x86_64')); -gulp.task('clean-vscode-linux-arm-rpm', util.rimraf('.build/linux/rpm/armhf')); -gulp.task('clean-vscode-linux-arm64-rpm', util.rimraf('.build/linux/rpm/arm64')); -gulp.task('clean-vscode-linux-ia32-snap', util.rimraf('.build/linux/snap/x64')); -gulp.task('clean-vscode-linux-x64-snap', util.rimraf('.build/linux/snap/x64')); -gulp.task('clean-vscode-linux-arm-snap', util.rimraf('.build/linux/snap/x64')); -gulp.task('clean-vscode-linux-arm64-snap', util.rimraf('.build/linux/snap/x64')); - -gulp.task('vscode-linux-ia32-prepare-deb', ['clean-vscode-linux-ia32-deb'], prepareDebPackage('ia32')); -gulp.task('vscode-linux-x64-prepare-deb', ['clean-vscode-linux-x64-deb'], prepareDebPackage('x64')); -gulp.task('vscode-linux-arm-prepare-deb', ['clean-vscode-linux-arm-deb'], prepareDebPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-deb', ['clean-vscode-linux-arm64-deb'], prepareDebPackage('arm64')); +gulp.task('vscode-linux-ia32-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/i386'), prepareDebPackage('ia32'))); +gulp.task('vscode-linux-x64-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/amd64'), prepareDebPackage('x64'))); +gulp.task('vscode-linux-arm-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/armhf'), prepareDebPackage('arm'))); +gulp.task('vscode-linux-arm64-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/arm64'), prepareDebPackage('arm64'))); gulp.task('vscode-linux-ia32-build-deb', ['vscode-linux-ia32-prepare-deb'], buildDebPackage('ia32')); gulp.task('vscode-linux-x64-build-deb', ['vscode-linux-x64-prepare-deb'], buildDebPackage('x64')); gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('arm')); gulp.task('vscode-linux-arm64-build-deb', ['vscode-linux-arm64-prepare-deb'], buildDebPackage('arm64')); -gulp.task('vscode-linux-ia32-prepare-rpm', ['clean-vscode-linux-ia32-rpm'], prepareRpmPackage('ia32')); -gulp.task('vscode-linux-x64-prepare-rpm', ['clean-vscode-linux-x64-rpm'], prepareRpmPackage('x64')); -gulp.task('vscode-linux-arm-prepare-rpm', ['clean-vscode-linux-arm-rpm'], prepareRpmPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-rpm', ['clean-vscode-linux-arm64-rpm'], prepareRpmPackage('arm64')); +gulp.task('vscode-linux-ia32-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/i386'), prepareRpmPackage('ia32'))); +gulp.task('vscode-linux-x64-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/x86_64'), prepareRpmPackage('x64'))); +gulp.task('vscode-linux-arm-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/armhf'), prepareRpmPackage('arm'))); +gulp.task('vscode-linux-arm64-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/arm64'), prepareRpmPackage('arm64'))); gulp.task('vscode-linux-ia32-build-rpm', ['vscode-linux-ia32-prepare-rpm'], buildRpmPackage('ia32')); gulp.task('vscode-linux-x64-build-rpm', ['vscode-linux-x64-prepare-rpm'], buildRpmPackage('x64')); gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('arm')); gulp.task('vscode-linux-arm64-build-rpm', ['vscode-linux-arm64-prepare-rpm'], buildRpmPackage('arm64')); -gulp.task('vscode-linux-ia32-prepare-snap', ['clean-vscode-linux-ia32-snap'], prepareSnapPackage('ia32')); -gulp.task('vscode-linux-x64-prepare-snap', ['clean-vscode-linux-x64-snap'], prepareSnapPackage('x64')); -gulp.task('vscode-linux-arm-prepare-snap', ['clean-vscode-linux-arm-snap'], prepareSnapPackage('arm')); -gulp.task('vscode-linux-arm64-prepare-snap', ['clean-vscode-linux-arm64-snap'], prepareSnapPackage('arm64')); +gulp.task('vscode-linux-ia32-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('ia32'))); +gulp.task('vscode-linux-x64-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('x64'))); +gulp.task('vscode-linux-arm-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('arm'))); +gulp.task('vscode-linux-arm64-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('arm64'))); gulp.task('vscode-linux-ia32-build-snap', ['vscode-linux-ia32-prepare-snap'], buildSnapPackage('ia32')); gulp.task('vscode-linux-x64-build-snap', ['vscode-linux-x64-prepare-snap'], buildSnapPackage('x64')); gulp.task('vscode-linux-arm-build-snap', ['vscode-linux-arm-prepare-snap'], buildSnapPackage('arm')); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 1cfc5b4c4f7..7973743d77d 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -105,8 +105,8 @@ function buildWin32Setup(arch, target) { } function defineWin32SetupTasks(arch, target) { - gulp.task(`clean-vscode-win32-${arch}-${target}-setup`, util.rimraf(setupDir(arch, target))); - gulp.task(`vscode-win32-${arch}-${target}-setup`, [`clean-vscode-win32-${arch}-${target}-setup`], buildWin32Setup(arch, target)); + const cleanTask = () => util.primraf(setupDir(arch, target)); + gulp.task(`vscode-win32-${arch}-${target}-setup`, util.task.series(cleanTask, buildWin32Setup(arch, target))); } defineWin32SetupTasks('ia32', 'system'); diff --git a/build/lib/util.js b/build/lib/util.js index fdb0f815e75..90b1e622cb1 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -198,32 +198,50 @@ function primraf(dir) { }); } exports.primraf = primraf; -/** - * Convert a stream to a promise. - */ -function streamToPromise(stream) { - return new Promise((resolve, reject) => { - stream.on('end', _ => resolve()); - stream.on('error', err => reject(err)); - }); -} -exports.streamToPromise = streamToPromise; var task; -(function (task) { +(function (task_1) { + function _isPromise(p) { + if (typeof p.then === 'function') { + return true; + } + return false; + } + async function _execute(task) { + // Always invoke as if it were a callback task + return new Promise((resolve, reject) => { + const taskResult = task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + if (typeof taskResult === 'undefined') { + // this was a callback task + return; + } + if (_isPromise(taskResult)) { + // this was a promise returning task + taskResult.then(resolve, reject); + return; + } + taskResult.on('end', _ => resolve()); + taskResult.on('error', err => reject(err)); + }); + } function series(...tasks) { return async () => { for (let i = 0; i < tasks.length; i++) { - await tasks[i](); + await _execute(tasks[i]); } }; } - task.series = series; + task_1.series = series; function parallel(...tasks) { return async () => { - await Promise.all(tasks.map(t => t())); + await Promise.all(tasks.map(t => _execute(t))); }; } - task.parallel = parallel; + task_1.parallel = parallel; })(task = exports.task || (exports.task = {})); function getVersion(root) { let version = process.env['BUILD_SOURCEVERSION']; diff --git a/build/lib/util.ts b/build/lib/util.ts index b831c3e8ed2..e0d56b59e77 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -252,30 +252,57 @@ export function primraf(dir: string): Promise { }); } -/** - * Convert a stream to a promise. - */ -export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { - return new Promise((resolve, reject) => { - stream.on('end', _ => resolve()); - stream.on('error', err => reject(err)); - }); -} - export type PromiseTask = () => Promise; +export type StreamTask = () => NodeJS.ReadWriteStream; +export type CallbackTask = (cb: (err?: any) => void) => void; +export type Task = PromiseTask | StreamTask | CallbackTask; export namespace task { - export function series(...tasks: PromiseTask[]): () => Promise { + + function _isPromise(p: Promise | NodeJS.ReadWriteStream): p is Promise { + if (typeof (p).then === 'function') { + return true; + } + return false; + } + + async function _execute(task: Task): Promise { + // Always invoke as if it were a callback task + return new Promise((resolve, reject) => { + const taskResult = task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + + if (typeof taskResult === 'undefined') { + // this was a callback task + return; + } + + if (_isPromise(taskResult)) { + // this was a promise returning task + taskResult.then(resolve, reject); + return; + } + + taskResult.on('end', _ => resolve()); + taskResult.on('error', err => reject(err)); + }); + } + + export function series(...tasks: Task[]): () => Promise { return async () => { for (let i = 0; i < tasks.length; i++) { - await tasks[i](); + await _execute(tasks[i]); } }; } - export function parallel(...tasks: PromiseTask[]): () => Promise { + export function parallel(...tasks: Task[]): () => Promise { return async () => { - await Promise.all(tasks.map(t => t())); + await Promise.all(tasks.map(t => _execute(t))); }; } } From cf19bcf86258da66130a028f4c8d987c6d8a143c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 17:34:12 +0100 Subject: [PATCH 0988/1066] More task simplifications (#67863) --- build/gulpfile.editor.js | 5 ++--- build/gulpfile.vscode.js | 11 +++++------ build/gulpfile.vscode.win32.js | 7 ++----- gulpfile.js | 8 +++----- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 3fc992bd9c4..e9bb8cddf52 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -63,8 +63,7 @@ var BUNDLED_FILE_HEADER = [ const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); -gulp.task('clean-editor-src', util.rimraf('out-editor-src')); -gulp.task('extract-editor-src', ['clean-editor-src'], function () { +gulp.task('extract-editor-src', util.task.series(util.rimraf('out-editor-src'), function () { console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); const apiusages = monacoapi.execute().usageContent; const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); @@ -99,7 +98,7 @@ gulp.task('extract-editor-src', ['clean-editor-src'], function () { importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, destRoot: path.join(root, 'out-editor-src') }); -}); +})); // Full compile, including nls and inline sources in sourcemaps, for build gulp.task('clean-editor-build', util.rimraf('out-editor-build')); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 0bf3c4a38d9..e81b0925f64 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -197,12 +197,11 @@ function getElectron(arch) { }; } -gulp.task('clean-electron', util.rimraf('.build/electron')); -gulp.task('electron', ['clean-electron'], getElectron(process.arch)); -gulp.task('electron-ia32', ['clean-electron'], getElectron('ia32')); -gulp.task('electron-x64', ['clean-electron'], getElectron('x64')); -gulp.task('electron-arm', ['clean-electron'], getElectron('arm')); -gulp.task('electron-arm64', ['clean-electron'], getElectron('arm64')); +gulp.task('electron', util.task.series(util.rimraf('.build/electron'), getElectron(process.arch))); +gulp.task('electron-ia32', util.task.series(util.rimraf('.build/electron'), getElectron('ia32'))); +gulp.task('electron-x64', util.task.series(util.rimraf('.build/electron'), getElectron('x64'))); +gulp.task('electron-arm', util.task.series(util.rimraf('.build/electron'), getElectron('arm'))); +gulp.task('electron-arm64', util.task.series(util.rimraf('.build/electron'), getElectron('arm64'))); /** diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 7973743d77d..3b3a21c1b69 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -124,11 +124,8 @@ function archiveWin32Setup(arch) { }; } -gulp.task('clean-vscode-win32-ia32-archive', util.rimraf(zipDir('ia32'))); -gulp.task('vscode-win32-ia32-archive', ['clean-vscode-win32-ia32-archive'], archiveWin32Setup('ia32')); - -gulp.task('clean-vscode-win32-x64-archive', util.rimraf(zipDir('x64'))); -gulp.task('vscode-win32-x64-archive', ['clean-vscode-win32-x64-archive'], archiveWin32Setup('x64')); +gulp.task('vscode-win32-ia32-archive', util.task.series(util.rimraf(zipDir('ia32')), archiveWin32Setup('ia32'))); +gulp.task('vscode-win32-x64-archive', util.task.series(util.rimraf(zipDir('x64')), archiveWin32Setup('x64'))); function copyInnoUpdater(arch) { return () => { diff --git a/gulpfile.js b/gulpfile.js index 882d7f84147..71e1618ae43 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,13 +14,11 @@ const path = require('path'); const compilation = require('./build/lib/compilation'); // Fast compile for development time -gulp.task('clean-client', util.rimraf('out')); -gulp.task('compile-client', ['clean-client'], compilation.compileTask('src', 'out', false)); -gulp.task('watch-client', ['clean-client'], compilation.watchTask('out', false)); +gulp.task('compile-client', util.task.series(util.rimraf('out'), compilation.compileTask('src', 'out', false))); +gulp.task('watch-client', util.task.series(util.rimraf('out'), compilation.watchTask('out', false))); // Full compile, including nls and inline sources in sourcemaps, for build -gulp.task('clean-client-build', util.rimraf('out-build')); -gulp.task('compile-client-build', ['clean-client-build'], compilation.compileTask('src', 'out-build', true)); +gulp.task('compile-client-build', util.task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); // Default gulp.task('default', ['compile']); From c0c719c6511d15160dd5d20b175f954f34bcc76e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 18:06:23 +0100 Subject: [PATCH 0989/1066] More gulp task work (#67863) --- build/gulpfile.extensions.js | 2 +- build/gulpfile.vscode.js | 32 +++++++++++++++++++++----------- build/lib/util.ts | 4 ++-- gulpfile.js | 6 ------ 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 64d5c213cc6..46fc1940bd5 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -143,4 +143,4 @@ const tasks = compilations.map(function (tsconfigFile) { gulp.task('compile-extensions', util.task.parallel(...tasks.map(t => t.compileTask))); gulp.task('watch-extensions', util.task.parallel(...tasks.map(t => t.watchTask))); -gulp.task('compile-extensions-build', util.task.parallel(...tasks.map(t => t.compileBuildTask))); +exports.compileExtensionsBuildTask = util.task.parallel(...tasks.map(t => t.compileBuildTask)); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index e81b0925f64..dde330fb9ea 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -33,6 +33,8 @@ const deps = require('./dependencies'); const getElectronVersion = require('./lib/electron').getElectronVersion; const createAsar = require('./lib/asar').createAsar; const minimist = require('minimist'); +const compilation = require('./lib/compilation'); +const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); // @ts-ignore @@ -86,17 +88,25 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -gulp.task('clean-optimized-vscode', util.rimraf('out-vscode')); -gulp.task('optimize-vscode', ['clean-optimized-vscode', 'compile-build', 'compile-extensions-build'], common.optimizeTask({ - src: 'out-build', - entryPoints: vscodeEntryPoints, - otherSources: [], - resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), - header: BUNDLED_FILE_HEADER, - out: 'out-vscode', - bundleInfo: undefined -})); +// Full compile, including nls and inline sources in sourcemaps, for build +const compileClientBuildTask = util.task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true)); + +// All Build +const compileBuildTask = util.task.parallel(compileClientBuildTask, compileExtensionsBuildTask); + +gulp.task('optimize-vscode', util.task.series( + util.task.parallel(util.rimraf('out-vscode'), compileBuildTask), + common.optimizeTask({ + src: 'out-build', + entryPoints: vscodeEntryPoints, + otherSources: [], + resources: vscodeResources, + loaderConfig: common.loaderConfig(nodeModules), + header: BUNDLED_FILE_HEADER, + out: 'out-vscode', + bundleInfo: undefined + })) +); gulp.task('optimize-index-js', ['optimize-vscode'], () => { diff --git a/build/lib/util.ts b/build/lib/util.ts index e0d56b59e77..4ff07196612 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -292,7 +292,7 @@ export namespace task { }); } - export function series(...tasks: Task[]): () => Promise { + export function series(...tasks: Task[]): PromiseTask { return async () => { for (let i = 0; i < tasks.length; i++) { await _execute(tasks[i]); @@ -300,7 +300,7 @@ export namespace task { }; } - export function parallel(...tasks: Task[]): () => Promise { + export function parallel(...tasks: Task[]): PromiseTask { return async () => { await Promise.all(tasks.map(t => _execute(t))); }; diff --git a/gulpfile.js b/gulpfile.js index 71e1618ae43..0f3fc9cc72c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -17,9 +17,6 @@ const compilation = require('./build/lib/compilation'); gulp.task('compile-client', util.task.series(util.rimraf('out'), compilation.compileTask('src', 'out', false))); gulp.task('watch-client', util.task.series(util.rimraf('out'), compilation.watchTask('out', false))); -// Full compile, including nls and inline sources in sourcemaps, for build -gulp.task('compile-client-build', util.task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true))); - // Default gulp.task('default', ['compile']); @@ -27,9 +24,6 @@ gulp.task('default', ['compile']); gulp.task('compile', ['monaco-typecheck', 'compile-client', 'compile-extensions']); gulp.task('watch', [/* 'monaco-typecheck-watch', */ 'watch-client', 'watch-extensions']); -// All Build -gulp.task('compile-build', ['compile-client-build', 'compile-extensions-build']); - process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); process.exit(1); From ca550379b9083aecfba3dbfb481e29cb98a00266 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Mon, 4 Feb 2019 09:24:11 -0800 Subject: [PATCH 0990/1066] Add dimensions APIs to terminal renderer --- src/vs/vscode.proposed.d.ts | 28 ++++++++++++++++++- src/vs/workbench/api/node/extHost.api.impl.ts | 4 +-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index e8d0f99c563..315b263c9f5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -888,13 +888,39 @@ declare module 'vscode' { //#region Terminal + /** + * An [event](#Event) which fires when a [Terminal](#Terminal)'s dimensions change. + */ + export interface TerminalDimensionsChangeEvent { + /** + * The [terminal](#Terminal) for which the dimensions have changed. + */ + readonly terminal: Terminal; + /** + * The new value for the [terminal's dimensions](#Terminal.dimensions). + */ + readonly dimensions: TerminalDimensions; + } + + namespace window { + /** + * An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change. + */ + export const onDidChangeTerminalDimensions: Event; + } + export interface Terminal { + /** + * The current dimensions of the terminal. + */ + readonly dimensions: TerminalDimensions; + /** * 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; + readonly onDidWriteData: Event; } /** diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index 59ecf348f91..f6d2526d4b4 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -479,9 +479,9 @@ export function createApiFactory( } return extHostTerminalService.createTerminal(nameOrOptions, shellPath, shellArgs); }, - createTerminalRenderer: proposedApiFunction(extension, (name: string) => { + createTerminalRenderer(name: string): vscode.TerminalRenderer { return extHostTerminalService.createTerminalRenderer(name); - }), + }, registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { return extHostTreeViews.registerTreeDataProvider(viewId, treeDataProvider, extension); }, From 70f3ce5de193d6bf1c6ed15b1f28eb7a3f076eac Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 4 Feb 2019 18:59:33 +0000 Subject: [PATCH 0991/1066] Fix #67605 - add new drag/drop to search tree --- .../parts/search/browser/searchResultsView.ts | 50 ++++++++++++++++++- .../parts/search/browser/searchView.ts | 5 +- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchResultsView.ts b/src/vs/workbench/parts/search/browser/searchResultsView.ts index 18a8d159b78..de850b8c323 100644 --- a/src/vs/workbench/parts/search/browser/searchResultsView.ts +++ b/src/vs/workbench/parts/search/browser/searchResultsView.ts @@ -8,7 +8,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CountBadge } from 'vs/base/browser/ui/countBadge/countBadge'; 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 { ITreeNode, ITreeRenderer, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { IAction } from 'vs/base/common/actions'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import * as paths from 'vs/base/common/paths'; @@ -26,6 +26,10 @@ 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 { IDragAndDropData } from 'vs/base/browser/dnd'; +import { fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { URI } from 'vs/base/common/uri'; interface IFolderMatchTemplate { label: IResourceLabel; @@ -333,3 +337,47 @@ export class SearchAccessibilityProvider implements IAccessibilityProvider { + constructor( + @IInstantiationService private instantiationService: IInstantiationService + ) { } + + onDragOver(data: IDragAndDropData, targetElement: RenderableMatch, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + return false; + } + + getDragURI(element: RenderableMatch): string | null { + if (element instanceof FileMatch) { + return element.remove.toString(); + } + + return null; + } + + getDragLabel?(elements: RenderableMatch[]): string | undefined { + if (elements.length > 1) { + return String(elements.length); + } + + const element = elements[0]; + return element instanceof FileMatch ? + resources.basename(element.resource()) : + undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + const elements = (data as ElementsDragAndDropData).elements; + const resources: URI[] = elements + .filter(e => e instanceof FileMatch) + .map((fm: FileMatch) => fm.resource()); + + if (resources.length) { + // Apply some datatransfer types to allow for dragging the element outside of the application + this.instantiationService.invokeFunction(fillResourceDataTransfers, resources, originalEvent); + } + } + + drop(data: IDragAndDropData, targetElement: RenderableMatch, targetIndex: number, originalEvent: DragEvent): void { + } +} diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index 25a2ec1b093..c55ba8be416 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -50,7 +50,7 @@ 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, getKeyboardEventForEditorOpen, RefreshAction } from 'vs/workbench/parts/search/browser/searchActions'; -import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate } from 'vs/workbench/parts/search/browser/searchResultsView'; +import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } 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'; @@ -633,7 +633,8 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { ], { identityProvider, - accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel) + accessibilityProvider: this.instantiationService.createInstance(SearchAccessibilityProvider, this.viewModel), + dnd: this.instantiationService.createInstance(SearchDND) })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); From 8816e58ca746adc3879716e722e3c8a5e30b09d3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 4 Feb 2019 19:43:23 +0000 Subject: [PATCH 0992/1066] Fix #66467 - don't restrict .* regex in search --- .../workbench/parts/search/browser/searchView.ts | 14 -------------- .../workbench/parts/search/browser/searchWidget.ts | 7 ++----- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index c55ba8be416..b492c49c74f 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -1143,20 +1143,6 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { return; } - // Validate regex is OK - if (isRegex) { - let regExp: RegExp; - try { - regExp = new RegExp(contentPattern); - } catch (e) { - return; // malformed regex - } - - if (strings.regExpLeadsToEndlessLoop(regExp)) { - return; // endless regex - } - } - const content: IPatternInfo = { pattern: contentPattern, isRegExp: isRegex, diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index 580a4a0044b..76ff4706fb6 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -397,15 +397,12 @@ export class SearchWidget extends Widget { if (!this.searchInput.getRegex()) { return null; } - let regExp: RegExp; try { - regExp = new RegExp(value); + // tslint:disable-next-line: no-unused-expression + new RegExp(value); } catch (e) { return { content: e.message }; } - if (strings.regExpLeadsToEndlessLoop(regExp)) { - return { content: nls.localize('regexp.validationFailure', "Expression matches everything") }; - } if (strings.regExpContainsBackreference(value)) { if (!this.searchConfiguration.usePCRE2) { From 737b6e45195215aa3e7e96bb624d87dbe53396fb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 4 Feb 2019 11:53:16 -0800 Subject: [PATCH 0993/1066] Strict null check all base Part of #60565. Make your time Doing more null supressions rather than code changes in quickOpen since we will be migrating off this old code --- src/tsconfig.strictNullChecks.json | 110 +--------------- .../parts/quickopen/browser/quickOpenModel.ts | 122 +++++++++--------- .../quickopen/browser/quickOpenWidget.ts | 2 +- .../base/parts/quickopen/common/quickOpen.ts | 2 +- .../parts/quickopen/common/quickOpenScorer.ts | 22 ++-- src/vs/base/parts/tree/browser/tree.ts | 6 +- 6 files changed, 82 insertions(+), 182 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index ef2000cae7e..3adcf237aed 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -6,9 +6,7 @@ }, "include": [ "./typings", - "./vs/base/browser/**/*.ts", - "./vs/base/common/**/*.ts", - "./vs/base/node/**/*.ts", + "./vs/base/**/*.ts", "./vs/editor/browser/**/*.ts", "./vs/editor/common/**/*.ts", "./vs/editor/contrib/codeAction/**/*.ts", @@ -21,110 +19,6 @@ "./vs/editor/test/**/*.ts" ], "files": [ - "./vs/base/parts/contextmenu/common/contextmenu.ts", - "./vs/base/parts/contextmenu/electron-browser/contextmenu.ts", - "./vs/base/parts/contextmenu/electron-main/contextmenu.ts", - "./vs/base/parts/ipc/electron-browser/ipc.electron-browser.ts", - "./vs/base/parts/ipc/electron-main/ipc.electron-main.ts", - "./vs/base/parts/ipc/node/ipc.cp.ts", - "./vs/base/parts/ipc/node/ipc.electron.ts", - "./vs/base/parts/ipc/node/ipc.net.ts", - "./vs/base/parts/ipc/node/ipc.ts", - "./vs/base/parts/ipc/test/node/ipc.cp.test.ts", - "./vs/base/parts/ipc/test/node/ipc.net.test.ts", - "./vs/base/parts/ipc/test/node/ipc.test.ts", - "./vs/base/parts/ipc/test/node/testApp.ts", - "./vs/base/parts/ipc/test/node/testService.ts", - "./vs/base/parts/quickopen/common/quickOpen.ts", - "./vs/base/parts/quickopen/common/quickOpenScorer.ts", - "./vs/base/parts/quickopen/test/common/quickOpenScorer.test.ts", - "./vs/base/parts/quickopen/browser/quickOpenWidget.ts", - "./vs/base/parts/tree/browser/tree.ts", - "./vs/base/parts/tree/browser/treeDefaults.ts", - "./vs/base/parts/tree/browser/treeDnd.ts", - "./vs/base/parts/tree/browser/treeImpl.ts", - "./vs/base/parts/tree/browser/treeModel.ts", - "./vs/base/parts/tree/browser/treeUtils.ts", - "./vs/base/parts/tree/browser/treeView.ts", - "./vs/base/parts/tree/browser/treeViewModel.ts", - "./vs/base/parts/tree/test/browser/treeModel.test.ts", - "./vs/base/parts/tree/test/browser/treeViewModel.test.ts", - "./vs/base/test/browser/browser.test.ts", - "./vs/base/test/browser/comparers.test.ts", - "./vs/base/test/browser/dom.test.ts", - "./vs/base/test/browser/highlightedLabel.test.ts", - "./vs/base/test/browser/htmlContent.test.ts", - "./vs/base/test/browser/progressBar.test.ts", - "./vs/base/test/browser/ui/contextview/contextview.test.ts", - "./vs/base/test/browser/ui/grid/grid.test.ts", - "./vs/base/test/browser/ui/grid/gridview.test.ts", - "./vs/base/test/browser/ui/grid/util.ts", - "./vs/base/test/browser/ui/list/listView.test.ts", - "./vs/base/test/browser/ui/list/rangeMap.test.ts", - "./vs/base/test/browser/ui/scrollbar/scrollableElement.test.ts", - "./vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts", - "./vs/base/test/browser/ui/splitview/splitview.test.ts", - "./vs/base/test/browser/ui/tree/asyncDataTree.test.ts", - "./vs/base/test/browser/ui/tree/dataTree.test.ts", - "./vs/base/test/browser/ui/tree/indexTreeModel.test.ts", - "./vs/base/test/browser/ui/tree/objectTree.test.ts", - "./vs/base/test/browser/ui/tree/objectTreeModel.test.ts", - "./vs/base/test/common/arrays.test.ts", - "./vs/base/test/common/assert.test.ts", - "./vs/base/test/common/async.test.ts", - "./vs/base/test/common/cache.test.ts", - "./vs/base/test/common/cancellation.test.ts", - "./vs/base/test/common/charCode.test.ts", - "./vs/base/test/common/collections.test.ts", - "./vs/base/test/common/color.test.ts", - "./vs/base/test/common/decorators.test.ts", - "./vs/base/test/common/diff/diff.test.ts", - "./vs/base/test/common/errors.test.ts", - "./vs/base/test/common/event.test.ts", - "./vs/base/test/common/filters.perf.test.ts", - "./vs/base/test/common/filters.test.ts", - "./vs/base/test/common/hash.test.ts", - "./vs/base/test/common/history.test.ts", - "./vs/base/test/common/json.test.ts", - "./vs/base/test/common/jsonEdit.test.ts", - "./vs/base/test/common/jsonFormatter.test.ts", - "./vs/base/test/common/keyCodes.test.ts", - "./vs/base/test/common/labels.test.ts", - "./vs/base/test/common/lifecycle.test.ts", - "./vs/base/test/common/linkedList.test.ts", - "./vs/base/test/common/map.test.ts", - "./vs/base/test/common/marshalling.test.ts", - "./vs/base/test/common/mime.test.ts", - "./vs/base/test/common/objects.test.ts", - "./vs/base/test/common/octicon.test.ts", - "./vs/base/test/common/paging.test.ts", - "./vs/base/test/common/paths.test.ts", - "./vs/base/test/common/resources.test.ts", - "./vs/base/test/common/scrollable.test.ts", - "./vs/base/test/common/strings.test.ts", - "./vs/base/test/common/types.test.ts", - "./vs/base/test/common/uri.test.ts", - "./vs/base/test/common/utils.ts", - "./vs/base/test/common/uuid.test.ts", - "./vs/base/test/node/config.test.ts", - "./vs/base/test/node/console.test.ts", - "./vs/base/test/node/decoder.test.ts", - "./vs/base/test/node/encoding/encoding.test.ts", - "./vs/base/test/node/extfs/extfs.test.ts", - "./vs/base/test/node/flow.test.ts", - "./vs/base/test/node/glob.test.ts", - "./vs/base/test/node/id.test.ts", - "./vs/base/test/node/pfs.test.ts", - "./vs/base/test/node/port.test.ts", - "./vs/base/test/node/processes/fixtures/fork.ts", - "./vs/base/test/node/processes/fixtures/fork_large.ts", - "./vs/base/test/node/processes/processes.test.ts", - "./vs/base/test/node/storage/storage.test.ts", - "./vs/base/test/node/stream/stream.test.ts", - "./vs/base/test/node/uri.test.perf.ts", - "./vs/base/test/node/utils.ts", - "./vs/base/worker/defaultWorkerFactory.ts", - "./vs/base/worker/workerMain.ts", "./vs/code/code.main.ts", "./vs/code/electron-browser/issue/issueReporterMain.ts", "./vs/code/electron-browser/issue/issueReporterModel.ts", @@ -893,4 +787,4 @@ "exclude": [ "./typings/require-monaco.d.ts" ] -} +} \ No newline at end of file diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 4959a71f185..7acd2cbbb9b 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -8,7 +8,7 @@ import * as types from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree'; import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import { Action, IAction, IActionRunner } from 'vs/base/common/actions'; import { compareAnything } from 'vs/base/common/comparers'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -35,15 +35,15 @@ let IDS = 0; export class QuickOpenItemAccessorClass implements IItemAccessor { - getItemLabel(entry: QuickOpenEntry): string { + getItemLabel(entry: QuickOpenEntry): string | null { return entry.getLabel(); } - getItemDescription(entry: QuickOpenEntry): string { + getItemDescription(entry: QuickOpenEntry): string | null { return entry.getDescription(); } - getItemPath(entry: QuickOpenEntry): string { + getItemPath(entry: QuickOpenEntry): string | undefined { const resource = entry.getResource(); return resource ? resource.fsPath : undefined; @@ -55,8 +55,8 @@ export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass(); export class QuickOpenEntry { private id: string; private labelHighlights: IHighlight[]; - private descriptionHighlights: IHighlight[]; - private detailHighlights: IHighlight[]; + private descriptionHighlights?: IHighlight[]; + private detailHighlights?: IHighlight[]; private hidden: boolean; constructor(highlights: IHighlight[] = []) { @@ -75,14 +75,14 @@ export class QuickOpenEntry { /** * The label of the entry to identify it from others in the list */ - getLabel(): string { + getLabel(): string | null { return null; } /** * The options for the label to use for this entry */ - getLabelOptions(): IIconLabelValueOptions { + getLabelOptions(): IIconLabelValueOptions | null { return null; } @@ -97,42 +97,42 @@ export class QuickOpenEntry { /** * Detail information about the entry that is optional and can be shown below the label */ - getDetail(): string { + getDetail(): string | null { return null; } /** * The icon of the entry to identify it from others in the list */ - getIcon(): string { + getIcon(): string | null { return null; } /** * A secondary description that is optional and can be shown right to the label */ - getDescription(): string { + getDescription(): string | null { return null; } /** * A tooltip to show when hovering over the entry. */ - getTooltip(): string { + getTooltip(): string | null { return null; } /** * A tooltip to show when hovering over the description portion of the entry. */ - getDescriptionTooltip(): string { + getDescriptionTooltip(): string | null { return null; } /** * An optional keybinding to show for an entry. */ - getKeybinding(): ResolvedKeybinding { + getKeybinding(): ResolvedKeybinding | null { return null; } @@ -140,7 +140,7 @@ export class QuickOpenEntry { * A resource for this entry. Resource URIs can be used to compare different kinds of entries and group * them together. */ - getResource(): URI { + getResource(): URI | null { return null; } @@ -170,7 +170,7 @@ export class QuickOpenEntry { /** * Allows to return highlight ranges that should show up for the entry label and description. */ - getHighlights(): [IHighlight[] /* Label */, IHighlight[] /* Description */, IHighlight[] /* Detail */] { + getHighlights(): [IHighlight[] /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] { return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights]; } @@ -180,7 +180,7 @@ export class QuickOpenEntry { * * The context parameter provides additional context information how the run was triggered. */ - run(mode: Mode, context: IContext): boolean { + run(mode: Mode, context: IEntryRunContext): boolean { return false; } @@ -195,9 +195,9 @@ export class QuickOpenEntry { } export class QuickOpenEntryGroup extends QuickOpenEntry { - private entry: QuickOpenEntry; - private groupLabel: string; - private withBorder: boolean; + private entry?: QuickOpenEntry; + private groupLabel?: string; + private withBorder?: boolean; constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) { super(); @@ -210,7 +210,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { /** * The label of the group or null if none. */ - getGroupLabel(): string { + getGroupLabel(): string | undefined { return this.groupLabel; } @@ -222,18 +222,18 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { * Whether to show a border on top of the group entry or not. */ showBorder(): boolean { - return this.withBorder; + return !!this.withBorder; } setShowBorder(showBorder: boolean): void { this.withBorder = showBorder; } - getLabel(): string { + getLabel(): string | null { return this.entry ? this.entry.getLabel() : super.getLabel(); } - getLabelOptions(): IIconLabelValueOptions { + getLabelOptions(): IIconLabelValueOptions | null { return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions(); } @@ -241,27 +241,27 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel(); } - getDetail(): string { + getDetail(): string | null { return this.entry ? this.entry.getDetail() : super.getDetail(); } - getResource(): URI { + getResource(): URI | null { return this.entry ? this.entry.getResource() : super.getResource(); } - getIcon(): string { + getIcon(): string | null { return this.entry ? this.entry.getIcon() : super.getIcon(); } - getDescription(): string { + getDescription(): string | null { return this.entry ? this.entry.getDescription() : super.getDescription(); } - getEntry(): QuickOpenEntry { + getEntry(): QuickOpenEntry | undefined { return this.entry; } - getHighlights(): [IHighlight[], IHighlight[], IHighlight[]] { + getHighlights(): [IHighlight[], IHighlight[] | undefined, IHighlight[] | undefined] { return this.entry ? this.entry.getHighlights() : super.getHighlights(); } @@ -277,7 +277,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden); } - run(mode: Mode, context: IContext): boolean { + run(mode: Mode, context: IEntryRunContext): boolean { return this.entry ? this.entry.run(mode, context) : super.run(mode, context); } } @@ -288,7 +288,7 @@ class NoActionProvider implements IActionProvider { return false; } - getActions(tree: ITree, element: any): IAction[] { + getActions(tree: ITree, element: any): IAction[] | null { return null; } @@ -296,7 +296,7 @@ class NoActionProvider implements IActionProvider { return false; } - getSecondaryActions(tree: ITree, element: any): IAction[] { + getSecondaryActions(tree: ITree, element: any): IAction[] | null { return null; } @@ -316,7 +316,7 @@ export interface IQuickOpenEntryTemplateData { } export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData { - group: HTMLDivElement; + group?: HTMLDivElement; } const templateEntry = 'quickOpenEntry'; @@ -325,9 +325,9 @@ const templateEntryGroup = 'quickOpenEntryGroup'; class Renderer implements IRenderer { private actionProvider: IActionProvider; - private actionRunner: IActionRunner; + private actionRunner?: IActionRunner; - constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner: IActionRunner | null = null) { + constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) { this.actionProvider = actionProvider; this.actionRunner = actionRunner; } @@ -356,7 +356,7 @@ class Renderer implements IRenderer { // Entry const row1 = DOM.$('.quick-open-row'); const row2 = DOM.$('.quick-open-row'); - const entry = DOM.$('.quick-open-entry', null, row1, row2); + const entry = DOM.$('.quick-open-entry', undefined, row1, row2); entryContainer.appendChild(entry); // Icon @@ -379,7 +379,7 @@ class Renderer implements IRenderer { const detail = new HighlightedLabel(detailContainer, true); // Entry Group - let group: HTMLDivElement; + let group: HTMLDivElement | undefined; if (templateId === templateEntryGroup) { group = document.createElement('div'); DOM.addClass(group, 'results-group'); @@ -442,7 +442,9 @@ class Renderer implements IRenderer { // Border if (group.showBorder()) { DOM.addClass(groupData.container, 'results-group-separator'); - groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString(); + if (styles.pickerGroupBorder) { + groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString(); + } } else { DOM.removeClass(groupData.container, 'results-group-separator'); groupData.container.style.borderTopColor = null; @@ -450,8 +452,12 @@ class Renderer implements IRenderer { // Group Label const groupLabel = group.getGroupLabel() || ''; - groupData.group.textContent = groupLabel; - groupData.group.style.color = styles.pickerGroupForeground.toString(); + if (groupData.group) { + groupData.group.textContent = groupLabel; + if (styles.pickerGroupForeground) { + groupData.group.style.color = styles.pickerGroupForeground.toString(); + } + } } // Normal Entry @@ -465,31 +471,31 @@ class Renderer implements IRenderer { // Label const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null); options.matches = labelHighlights || []; - options.title = entry.getTooltip(); - options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow + options.title = entry.getTooltip() || undefined; + options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription() || undefined; // tooltip over description because it could overflow options.descriptionMatches = descriptionHighlights || []; - data.label.setLabel(entry.getLabel(), entry.getDescription(), options); + data.label.setLabel(entry.getLabel() || undefined, entry.getDescription() || undefined, options); // Meta - data.detail.set(entry.getDetail(), detailHighlights); + data.detail.set(entry.getDetail() || undefined, detailHighlights); // Keybinding - data.keybinding.set(entry.getKeybinding()); + data.keybinding.set(entry.getKeybinding()!); } } disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void { const data = templateData as IQuickOpenEntryGroupTemplateData; data.actionBar.dispose(); - data.actionBar = null; - data.container = null; - data.entry = null; - data.keybinding = null; - data.detail = null; - data.group = null; - data.icon = null; + data.actionBar = null!; + data.container = null!; + data.entry = null!; + data.keybinding = null!; + data.detail = null!; + data.group = null!; + data.icon = null!; data.label.dispose(); - data.label = null; + data.label = null!; } } @@ -562,7 +568,7 @@ export class QuickOpenModel implements return entry.getId(); } - getLabel(entry: QuickOpenEntry): string { + getLabel(entry: QuickOpenEntry): string | null { return entry.getLabel(); } @@ -579,7 +585,7 @@ export class QuickOpenModel implements return !entry.isHidden(); } - run(entry: QuickOpenEntry, mode: Mode, context: IContext): boolean { + run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean { return entry.run(mode, context); } } @@ -603,8 +609,8 @@ export function compareEntries(elementA: QuickOpenEntry, elementB: QuickOpenEntr } // Fallback to the full path if labels are identical and we have associated resources - let nameA = elementA.getLabel(); - let nameB = elementB.getLabel(); + let nameA = elementA.getLabel()!; + let nameB = elementB.getLabel()!; if (nameA === nameB) { const resourceA = elementA.getResource(); const resourceB = elementB.getResource(); diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index c121e1d3b58..a023136828e 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -681,7 +681,7 @@ export class QuickOpenWidget extends Disposable implements IModelProvider { const prefix = autoFocus.autoFocusPrefixMatch; const lowerCasePrefix = prefix.toLowerCase(); for (const entry of entries) { - const label = input.dataSource.getLabel(entry); + const label = input.dataSource.getLabel(entry) || ''; if (!caseSensitiveMatch && label.indexOf(prefix) === 0) { caseSensitiveMatch = entry; diff --git a/src/vs/base/parts/quickopen/common/quickOpen.ts b/src/vs/base/parts/quickopen/common/quickOpen.ts index a7b46b23c46..dd39a215ef1 100644 --- a/src/vs/base/parts/quickopen/common/quickOpen.ts +++ b/src/vs/base/parts/quickopen/common/quickOpen.ts @@ -59,7 +59,7 @@ export interface IKeyMods { export interface IDataSource { getId(entry: T): string; - getLabel(entry: T): string; + getLabel(entry: T): string | null; } /** diff --git a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts index 9abbda5ac13..c1c1a82673e 100644 --- a/src/vs/base/parts/quickopen/common/quickOpenScorer.ts +++ b/src/vs/base/parts/quickopen/common/quickOpenScorer.ts @@ -285,17 +285,17 @@ export interface IItemAccessor { /** * Just the label of the item to score on. */ - getItemLabel(item: T): string; + getItemLabel(item: T): string | null; /** * The optional description of the item to score on. Can be null. */ - getItemDescription(item: T): string; + getItemDescription(item: T): string | null; /** * If the item is a file, the path of the file to score on. Can be null. */ - getItemPath(file: T): string; + getItemPath(file: T): string | undefined; } const PATH_IDENTITY_SCORE = 1 << 18; @@ -376,10 +376,10 @@ function createMatches(offsets: undefined | number[]): IMatch[] { return ret; } -function doScoreItem(label: string, description: string, path: string, query: IPreparedQuery, fuzzy: boolean): IItemScore { +function doScoreItem(label: string, description: string | null, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore { // 1.) treat identity matches on full path highest - if (path && isLinux ? query.original === path : equalsIgnoreCase(query.original, path)) { + if (path && (isLinux ? query.original === path : equalsIgnoreCase(query.original, path))) { return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined }; } @@ -469,8 +469,8 @@ export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery return scoreA === LABEL_PREFIX_SCORE ? -1 : 1; } - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; // prefer shorter names when both match on label prefix if (labelA.length !== labelB.length) { @@ -484,8 +484,8 @@ export function compareItemsByScore(itemA: T, itemB: T, query: IPreparedQuery return scoreA === LABEL_CAMELCASE_SCORE ? -1 : 1; } - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; // prefer more compact camel case matches over longer const comparedByMatchLength = compareByMatchLength(itemScoreA.labelMatch, itemScoreB.labelMatch); @@ -592,8 +592,8 @@ function compareByMatchLength(matchesA?: IMatch[], matchesB?: IMatch[]): number export function fallbackCompare(itemA: T, itemB: T, query: IPreparedQuery, accessor: IItemAccessor): number { // check for label + description length and prefer shorter - const labelA = accessor.getItemLabel(itemA); - const labelB = accessor.getItemLabel(itemB); + const labelA = accessor.getItemLabel(itemA) || ''; + const labelB = accessor.getItemLabel(itemB) || ''; const descriptionA = accessor.getItemDescription(itemA); const descriptionB = accessor.getItemDescription(itemB); diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index a74f5cad56e..9966687e74b 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -716,12 +716,12 @@ export interface IActionProvider { /** * Returns whether or not the element has actions. These show up in place right to the element in the tree. */ - hasActions(tree: ITree, element: any): boolean; + hasActions(tree: ITree | null, element: any): boolean; /** * Returns a promise of an array with the actions of the element that should show up in place right to the element in the tree. */ - getActions(tree: ITree, element: any): IAction[]; + getActions(tree: ITree | null, element: any): IAction[] | null; /** * Returns whether or not the element has secondary actions. These show up once the user has expanded the element's action bar. @@ -731,7 +731,7 @@ export interface IActionProvider { /** * Returns a promise of an array with the secondary actions of the element that should show up once the user has expanded the element's action bar. */ - getSecondaryActions(tree: ITree, element: any): IAction[]; + getSecondaryActions(tree: ITree, element: any): IAction[] | null; /** * Returns an action item to render an action. From 5e03d02484d715a406d21016b684ef5ecc66a230 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 4 Feb 2019 12:52:57 -0800 Subject: [PATCH 0994/1066] Working on strict null checks in vs/code --- src/tsconfig.strictNullChecks.json | 1 + src/vs/code/electron-main/app.ts | 32 +++++++++---------- src/vs/code/electron-main/main.ts | 6 ++-- src/vs/code/electron-main/window.ts | 28 +++++++++------- src/vs/code/electron-main/windows.ts | 20 ++++++------ src/vs/platform/windows/common/windows.ts | 2 +- .../platform/windows/electron-main/windows.ts | 8 ++--- 7 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 3adcf237aed..fc15521231c 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -37,6 +37,7 @@ "./vs/code/electron-main/logUploader.ts", "./vs/code/electron-main/sharedProcess.ts", "./vs/code/electron-main/theme.ts", + "./vs/code/electron-main/window.ts", "./vs/code/node/cli.ts", "./vs/code/node/cliProcessMain.ts", "./vs/code/node/paths.ts", diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index ce0240ad7d2..8a2f82f82ab 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -16,7 +16,6 @@ import { UpdateChannel } from 'vs/platform/update/node/updateIpc'; import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron-main'; import { Server, connect, Client } from 'vs/base/parts/ipc/node/ipc.net'; import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; -import { Mutex } from 'windows-mutex'; import { LaunchService, LaunchChannel, ILaunchService } from 'vs/platform/launch/electron-main/launchService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -484,22 +483,22 @@ export class CodeApplication extends Disposable { // written from the main process once. const telemetryInstanceId = 'telemetry.instanceId'; - const instanceId = storageMainService.get(telemetryInstanceId, null); - if (instanceId === null) { + const instanceId = storageMainService.get(telemetryInstanceId, undefined); + if (instanceId === undefined) { storageMainService.store(telemetryInstanceId, generateUuid()); } const telemetryFirstSessionDate = 'telemetry.firstSessionDate'; - const firstSessionDate = storageMainService.get(telemetryFirstSessionDate, null); - if (firstSessionDate === null) { + const firstSessionDate = storageMainService.get(telemetryFirstSessionDate, undefined); + if (firstSessionDate === undefined) { storageMainService.store(telemetryFirstSessionDate, new Date().toUTCString()); } const telemetryCurrentSessionDate = 'telemetry.currentSessionDate'; const telemetryLastSessionDate = 'telemetry.lastSessionDate'; - const lastSessionDate = storageMainService.get(telemetryCurrentSessionDate, null); // previous session date was the "current" one at that time + const lastSessionDate = storageMainService.get(telemetryCurrentSessionDate, undefined); // previous session date was the "current" one at that time const currentSessionDate = new Date().toUTCString(); // current session date is "now" - storageMainService.store(telemetryLastSessionDate, lastSessionDate); + storageMainService.store(telemetryLastSessionDate, typeof lastSessionDate === 'undefined' ? null : lastSessionDate); storageMainService.store(telemetryCurrentSessionDate, currentSessionDate); }); } @@ -590,7 +589,7 @@ export class CodeApplication extends Disposable { // Watch Electron URLs and forward them to the UrlService const args = this.environmentService.args; const urls = args['open-url'] ? args._urls : []; - const urlListener = new ElectronURLListener(urls, urlService, this.windowsMainService); + const urlListener = new ElectronURLListener(urls || [], urlService, this.windowsMainService); this._register(urlListener); this.windowsMainService.ready(this.userEnv); @@ -617,13 +616,12 @@ export class CodeApplication extends Disposable { const windowsMainService = accessor.get(IWindowsMainService); const historyMainService = accessor.get(IHistoryMainService); - let windowsMutex: Mutex | null = null; if (isWindows) { // Setup Windows mutex try { const Mutex = (require.__$__nodeRequire('windows-mutex') as any).Mutex; - windowsMutex = new Mutex(product.win32MutexName); + const windowsMutex = new Mutex(product.win32MutexName); this._register(toDisposable(() => windowsMutex.release())); } catch (e) { if (!this.environmentService.isBuilt) { @@ -709,7 +707,7 @@ export class CodeApplication extends Disposable { const resolveAuthority = (authority: string): ResolvedAuthority | null => { if (authority.indexOf('+') >= 0) { if (resolvedAuthorities.has(authority)) { - return resolvedAuthorities.get(authority); + return resolvedAuthorities.get(authority) || null; } return null; } else { @@ -721,24 +719,24 @@ export class CodeApplication extends Disposable { protocol.registerBufferProtocol(REMOTE_HOST_SCHEME, async (request, callback) => { if (request.method !== 'GET') { - return callback(null); + return callback(undefined); } const uri = URI.parse(request.url); - let activeConnection: ActiveConnection = null; + let activeConnection: ActiveConnection | undefined; if (connectionPool.has(uri.authority)) { activeConnection = connectionPool.get(uri.authority); } else { let resolvedAuthority = resolveAuthority(uri.authority); if (!resolvedAuthority) { - callback(null); + callback(undefined); return; } activeConnection = new ActiveConnection(uri.authority, resolvedAuthority.host, resolvedAuthority.port); connectionPool.set(uri.authority, activeConnection); } try { - const rawClient = await activeConnection.getClient(); + const rawClient = await activeConnection!.getClient(); if (connectionPool.has(uri.authority)) { // not disposed in the meantime const channel = rawClient.getChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME); @@ -746,11 +744,11 @@ export class CodeApplication extends Disposable { const fileContents = await channel.call('readFile', [uri]); callback(Buffer.from(fileContents)); } else { - callback(null); + callback(undefined); } } catch (err) { errors.onUnexpectedError(err); - callback(null); + callback(undefined); } }); } diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 39f624e5de8..4874979725f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -158,7 +158,7 @@ function setupIPC(accessor: ServicesAccessor): Promise { logService.trace('Sending env to running instance...'); return allowSetForegroundWindow(service) - .then(() => service.start(environmentService.args, process.env)) + .then(() => service.start(environmentService.args, process.env as platform.IProcessEnvironment)) .then(() => client.dispose()) .then(() => { @@ -316,14 +316,14 @@ function createServices(args: ParsedArgs, bufferLogService: BufferLogService): I function initServices(environmentService: IEnvironmentService, stateService: StateService): Promise { // Ensure paths for environment service exist - const environmentServiceInitialization = Promise.all([ + const environmentServiceInitialization = Promise.all([ environmentService.extensionsPath, environmentService.nodeCachedDataDir, environmentService.logsPath, environmentService.globalStorageHome, environmentService.workspaceStorageHome, environmentService.backupHome - ].map(path => path && mkdirp(path))); + ].map((path): undefined | Promise => path ? mkdirp(path) : undefined)); // State service const stateServiceInitialization = stateService.init(); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index dbe19cc5743..af7b8265a45 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -73,7 +73,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private whenReadyCallbacks: { (window: ICodeWindow): void }[]; private currentConfig: IWindowConfiguration; - private pendingLoadConfig: IWindowConfiguration; + private pendingLoadConfig?: IWindowConfiguration; private marketplaceHeadersPromise: Promise; @@ -205,7 +205,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return !!this.config.extensionTestsPath; } - get extensionDevelopmentPath(): string { + get extensionDevelopmentPath(): string | undefined { return this.config.extensionDevelopmentPath; } @@ -253,19 +253,19 @@ export class CodeWindow extends Disposable implements ICodeWindow { return this._lastFocusTime; } - get backupPath(): string { + get backupPath(): string | undefined { return this.currentConfig ? this.currentConfig.backupPath : undefined; } - get openedWorkspace(): IWorkspaceIdentifier { + get openedWorkspace(): IWorkspaceIdentifier | undefined { return this.currentConfig ? this.currentConfig.workspace : undefined; } - get openedFolderUri(): URI { + get openedFolderUri(): URI | undefined { return this.currentConfig ? this.currentConfig.folderUri : undefined; } - get remoteAuthority(): string { + get remoteAuthority(): string | undefined { return this.currentConfig ? this.currentConfig.remoteAuthority : undefined; } @@ -310,7 +310,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private registerListeners(): void { // Prevent loading of svgs - this._win.webContents.session.webRequest.onBeforeRequest(null, (details, callback) => { + this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => { if (details.url.indexOf('.svg') > 0) { const uri = URI.parse(details.url); if (uri && !uri.scheme.match(/file/i) && (uri.path as any).endsWith('.svg')) { @@ -321,7 +321,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { return callback({}); }); - this._win.webContents.session.webRequest.onHeadersReceived(null, (details: any, callback: any) => { + this._win.webContents.session.webRequest.onHeadersReceived(null!, (details: any, callback: any) => { const contentType: string[] = (details.responseHeaders['content-type'] || details.responseHeaders['Content-Type']) as any; if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { return callback({ cancel: true }); @@ -338,7 +338,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (this.pendingLoadConfig) { this.currentConfig = this.pendingLoadConfig; - this.pendingLoadConfig = null; + this.pendingLoadConfig = undefined; } // To prevent flashing, we set the window visible after the page has finished to load but before Code is loaded @@ -685,7 +685,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private restoreWindowState(state?: IWindowState): IWindowState { if (state) { try { - state = this.validateWindowState(state); + state = this.validateWindowState(state) || undefined; } catch (err) { this.logService.warn(`Unexpected error validating window state: ${err}\n${err.stack}`); // somehow display API can be picky about the state to validate } @@ -703,7 +703,11 @@ export class CodeWindow extends Disposable implements ICodeWindow { return null; } - if ([state.x, state.y, state.width, state.height].some(n => typeof n !== 'number')) { + if (typeof state.x !== 'number' + || typeof state.y !== 'number' + || typeof state.width !== 'number' + || typeof state.height !== 'number' + ) { return null; } @@ -1042,6 +1046,6 @@ export class CodeWindow extends Disposable implements ICodeWindow { clearTimeout(this.showTimeoutHandle); } - this._win = null; // Important to dereference the window object to allow for GC + this._win = null!; // Important to dereference the window object to allow for GC } } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 9c2ff750b69..6b4a7ae736e 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -368,12 +368,12 @@ export class WindowsManager implements IWindowsMainService { // folders that should be added to the currently active window. let foldersToAdd: URI[] = []; if (openConfig.addMode) { - foldersToAdd = pathsToOpen.filter(path => !!path.folderUri).map(path => path.folderUri); + foldersToAdd = pathsToOpen.filter(path => !!path.folderUri).map(path => path.folderUri!); pathsToOpen = pathsToOpen.filter(path => !path.folderUri); } // collect all file inputs - let fileInputs: IFileInputs = undefined; + let fileInputs: IFileInputs | undefined; for (const path of pathsToOpen) { if (path.fileUri) { if (!fileInputs) { @@ -973,7 +973,7 @@ export class WindowsManager implements IWindowsMainService { return restoreWindows; } - private argToUri(arg: string): URI { + private argToUri(arg: string): URI | null { try { let uri = URI.parse(arg); if (!uri.scheme) { @@ -987,7 +987,7 @@ export class WindowsManager implements IWindowsMainService { return null; } - private parseUri(uri: URI, isFile: boolean, options?: IPathParseOptions): IPathToOpen { + private parseUri(uri: URI, isFile: boolean, options?: IPathParseOptions): IPathToOpen | null { if (!uri || !uri.scheme) { return null; } @@ -1025,7 +1025,7 @@ export class WindowsManager implements IWindowsMainService { }; } - private parsePath(anyPath: string, options?: IPathParseOptions): IPathToOpen { + private parsePath(anyPath: string, options?: IPathParseOptions): IPathToOpen | null { if (!anyPath) { return null; } @@ -1212,7 +1212,7 @@ export class WindowsManager implements IWindowsMainService { configuration.backupPath = join(this.environmentService.backupHome, options.emptyWindowBackupInfo.backupFolder); } - let window: ICodeWindow; + let window: ICodeWindow | undefined; if (!options.forceNewWindow && !options.forceNewTabbedWindow) { window = options.windowToUse || this.getLastActiveWindow(); if (window) { @@ -1372,7 +1372,7 @@ export class WindowsManager implements IWindowsMainService { // // We want the new window to open on the same display that the last active one is in - let displayToUse: Electron.Display; + let displayToUse: Electron.Display | undefined; const displays = screen.getAllDisplays(); // Single Display @@ -1566,7 +1566,7 @@ export class WindowsManager implements IWindowsMainService { closeListener.dispose(); loadListener.dispose(); - resolve(null); + resolve(); } } @@ -2005,7 +2005,7 @@ class WorkspacesManager { window.focus(); // Register window for backups and migrate current backups over - let backupPath: string; + let backupPath: string | undefined; if (!window.config.extensionDevelopmentPath) { backupPath = this.backupMainService.registerWorkspaceBackupSync(workspace, window.config.backupPath); } @@ -2110,7 +2110,7 @@ class WorkspacesManager { }); } - private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { + private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string | undefined { if (workspace) { if (isSingleFolderWorkspaceIdentifier(workspace)) { return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined; diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 56eaeef6f49..8e643286d88 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -31,7 +31,7 @@ export interface INativeOpenDialogOptions { export interface IEnterWorkspaceResult { workspace: IWorkspaceIdentifier; - backupPath: string; + backupPath?: string; } export interface CrashReporterStartOptions { diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index e067b62e96d..19219a64149 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -33,11 +33,11 @@ export interface ICodeWindow { readonly win: Electron.BrowserWindow; readonly config: IWindowConfiguration; - readonly openedFolderUri: URI; - readonly openedWorkspace: IWorkspaceIdentifier; - readonly backupPath: string; + readonly openedFolderUri?: URI; + readonly openedWorkspace?: IWorkspaceIdentifier; + readonly backupPath?: string; - readonly remoteAuthority: string; + readonly remoteAuthority?: string; readonly isExtensionDevelopmentHost: boolean; readonly isExtensionTestHost: boolean; From 68ac1ab84302dd627426a15404fce3fa959c315d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 4 Feb 2019 13:35:10 -0800 Subject: [PATCH 0995/1066] Strict null checking more quick open related files --- src/tsconfig.strictNullChecks.json | 4 +++ .../quickopen/browser/quickOpenWidget.ts | 2 +- .../browser/quickOpen/editorQuickOpen.ts | 8 ++--- .../browser/quickOpen/quickCommand.ts | 12 ++++---- .../quickOpen/quickOpenEditorWidget.ts | 4 +-- .../browser/quickOpen/quickOutline.ts | 29 ++++++++++--------- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index fc15521231c..edc118aff8d 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -154,6 +154,10 @@ "./vs/editor/standalone/browser/colorizer.ts", "./vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts", "./vs/editor/standalone/browser/inspectTokens/inspectTokens.ts", + "./vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts", + "./vs/editor/standalone/browser/quickOpen/quickCommand.ts", + "./vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts", + "./vs/editor/standalone/browser/quickOpen/quickOutline.ts", "./vs/editor/standalone/browser/simpleServices.ts", "./vs/editor/standalone/browser/standaloneCodeEditor.ts", "./vs/editor/standalone/browser/standaloneCodeServiceImpl.ts", diff --git a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts index a023136828e..ed4b43d2680 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenWidget.ts @@ -36,7 +36,7 @@ export interface IQuickOpenCallbacks { export interface IQuickOpenOptions extends IQuickOpenStyles { minItemsToShow?: number; maxItemsToShow?: number; - inputPlaceHolder: string; + inputPlaceHolder?: string; inputAriaLabel?: string; actionProvider?: IActionProvider; keyboardSupport?: boolean; diff --git a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts index 75de1e65c8d..0a77b555d7d 100644 --- a/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts +++ b/src/vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts @@ -31,9 +31,9 @@ export class QuickOpenController implements editorCommon.IEditorContribution, ID } private editor: ICodeEditor; - private widget: QuickOpenEditorWidget; - private rangeHighlightDecorationId: string; - private lastKnownEditorSelection: Selection; + private widget: QuickOpenEditorWidget | null; + private rangeHighlightDecorationId: string | null; + private lastKnownEditorSelection: Selection | null; constructor(editor: ICodeEditor, @IThemeService private readonly themeService: IThemeService) { this.editor = editor; @@ -83,7 +83,7 @@ export class QuickOpenController implements editorCommon.IEditorContribution, ID () => onClose(false), () => onClose(true), (value: string) => { - this.widget.setInput(opts.getModel(value), opts.getAutoFocus(value)); + this.widget!.setInput(opts.getModel(value), opts.getAutoFocus(value)); }, { inputAriaLabel: opts.inputAriaLabel diff --git a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts index 892d458a277..fdbf17a7685 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickCommand.ts @@ -8,8 +8,8 @@ import * as browser from 'vs/base/browser/browser'; import { onUnexpectedError } from 'vs/base/common/errors'; import { matchesFuzzy } from 'vs/base/common/filters'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; -import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { IEditor, IEditorAction } from 'vs/editor/common/editorCommon'; @@ -50,7 +50,7 @@ export class EditorActionCommandEntry extends QuickOpenEntryGroup { return this.key; } - public run(mode: Mode, context: IContext): boolean { + public run(mode: Mode, context: IEntryRunContext): boolean { if (mode === Mode.OPEN) { // Use a timeout to give the quick open widget a chance to close itself first @@ -112,8 +112,8 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { } private _sort(elementA: QuickOpenEntryGroup, elementB: QuickOpenEntryGroup): number { - let elementAName = elementA.getLabel().toLowerCase(); - let elementBName = elementB.getLabel().toLowerCase(); + let elementAName = (elementA.getLabel() || '').toLowerCase(); + let elementBName = (elementB.getLabel() || '').toLowerCase(); return elementAName.localeCompare(elementBName); } @@ -129,7 +129,7 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction { if (action.label) { let highlights = matchesFuzzy(searchValue, action.label); if (highlights) { - entries.push(new EditorActionCommandEntry(keybinding ? keybinding.getLabel() : '', keybinding ? keybinding.getAriaLabel() : '', highlights, action, editor)); + entries.push(new EditorActionCommandEntry(keybinding ? keybinding.getLabel() || '' : '', keybinding ? keybinding.getAriaLabel() || '' : '', highlights, action, editor)); } } } diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts index 34df6555f29..859e8cca21b 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts @@ -45,7 +45,7 @@ export class QuickOpenEditorWidget implements IOverlayWidget { onCancel: onCancel, onType: onType }, { - inputPlaceHolder: null, + inputPlaceHolder: undefined, inputAriaLabel: configuration.inputAriaLabel, keyboardSupport: true } @@ -98,7 +98,7 @@ export class QuickOpenEditorWidget implements IOverlayWidget { this.codeEditor.layoutOverlayWidget(this); } - public getPosition(): IOverlayWidgetPosition { + public getPosition(): IOverlayWidgetPosition | null { if (this.visible) { return { preference: OverlayWidgetPositionPreference.TOP_CENTER diff --git a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts index 7a75c155e2d..7a303f61636 100644 --- a/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts +++ b/src/vs/editor/standalone/browser/quickOpen/quickOutline.ts @@ -9,8 +9,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { matchesFuzzy } from 'vs/base/common/filters'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as strings from 'vs/base/common/strings'; -import { IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; -import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { IHighlight, QuickOpenEntryGroup, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -26,12 +26,12 @@ let SCOPE_PREFIX = ':'; export class SymbolEntry extends QuickOpenEntryGroup { private name: string; private type: string; - private description: string; + private description: string | null; private range: Range; private editor: ICodeEditor; private decorator: IDecorator; - constructor(name: string, type: string, description: string, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) { + constructor(name: string, type: string, description: string | null, range: Range, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator) { super(); this.name = name; @@ -55,7 +55,7 @@ export class SymbolEntry extends QuickOpenEntryGroup { return this.type; } - public getDescription(): string { + public getDescription(): string | null { return this.description; } @@ -67,7 +67,7 @@ export class SymbolEntry extends QuickOpenEntryGroup { return this.range; } - public run(mode: Mode, context: IContext): boolean { + public run(mode: Mode, context: IEntryRunContext): boolean { if (mode === Mode.OPEN) { return this.runOpen(context); } @@ -75,7 +75,7 @@ export class SymbolEntry extends QuickOpenEntryGroup { return this.runPreview(); } - private runOpen(context: IContext): boolean { + private runOpen(_context: IEntryRunContext): boolean { // Apply selection and focus let range = this.toSelection(); @@ -128,12 +128,15 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { }); } - public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + public run(accessor: ServicesAccessor, editor: ICodeEditor) { + if (!editor.hasModel()) { + return undefined; + } - let model = editor.getModel(); + const model = editor.getModel(); if (!DocumentSymbolProviderRegistry.has(model)) { - return null; + return undefined; } // Resolve outline @@ -166,7 +169,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { }); } - private symbolEntry(name: string, type: string, description: string, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry { + private symbolEntry(name: string, type: string, description: string | null, range: IRange, highlights: IHighlight[], editor: ICodeEditor, decorator: IDecorator): SymbolEntry { return new SymbolEntry(name, type, description, Range.lift(range), highlights, editor, decorator); } @@ -222,7 +225,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { // Update previous result with count if (currentResult) { - currentResult.setGroupLabel(this.typeToLabel(currentType, typeCounter)); + currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter)); } currentType = result.getType(); @@ -240,7 +243,7 @@ export class QuickOutlineAction extends BaseEditorQuickOpenAction { // Update previous result with count if (currentResult) { - currentResult.setGroupLabel(this.typeToLabel(currentType, typeCounter)); + currentResult.setGroupLabel(this.typeToLabel(currentType || '', typeCounter)); } } From 996b5c345b6254f253d783667c009c26841ca799 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Feb 2019 00:58:26 +0000 Subject: [PATCH 0996/1066] Remove SearchService#extendQuery, all search clients now use the QueryBuilder --- src/vs/platform/search/common/search.ts | 1 - .../electron-browser/mainThreadWorkspace.ts | 61 +++++-------------- .../services/search/node/searchService.ts | 21 +------ 3 files changed, 16 insertions(+), 67 deletions(-) diff --git a/src/vs/platform/search/common/search.ts b/src/vs/platform/search/common/search.ts index 877b69da34a..fa46ccc8958 100644 --- a/src/vs/platform/search/common/search.ts +++ b/src/vs/platform/search/common/search.ts @@ -27,7 +27,6 @@ export interface ISearchService { _serviceBrand: any; textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; fileSearch(query: IFileQuery, token?: CancellationToken): Promise; - extendQuery(query: ITextQuery | IFileQuery): void; clearCache(cacheKey: string): Promise; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; } diff --git a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts index 6a435ed4660..2b8311a2e3e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadWorkspace.ts @@ -3,27 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { isPromiseCanceledError } from 'vs/base/common/errors'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IFolderQuery, IPatternInfo, ISearchConfiguration, ISearchProgressItem, ISearchService, QueryType, IFileQuery, IFileMatch } from 'vs/platform/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/platform/search/common/search'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers'; -import { QueryBuilder, ITextQueryBuilderOptions } from 'vs/workbench/parts/search/common/queryBuilder'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/parts/search/common/queryBuilder'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol'; -import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { TextSearchComplete } from 'vscode'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape } from '../node/extHost.protocol'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -31,13 +30,13 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { private readonly _toDispose: IDisposable[] = []; private readonly _activeCancelTokens: { [id: number]: CancellationTokenSource } = Object.create(null); private readonly _proxy: ExtHostWorkspaceShape; + private readonly _queryBuilder = this._instantiationService.createInstance(QueryBuilder); constructor( extHostContext: IExtHostContext, @ISearchService private readonly _searchService: ISearchService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ITextFileService private readonly _textFileService: ITextFileService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IWorkspaceEditingService private readonly _workspaceEditingService: IWorkspaceEditingService, @IStatusbarService private readonly _statusbarService: IStatusbarService, @IWindowService private readonly _windowService: IWindowService, @@ -120,43 +119,14 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return undefined; } - let folderQueries: IFolderQuery[]; - if (includeFolder) { - folderQueries = [{ folder: includeFolder }]; // if base provided, only search in that folder - } else { - folderQueries = workspace.folders.map(folder => ({ folder: folder.uri })); // absolute pattern: search across all folders - } - - if (!folderQueries) { - return undefined; // invalid query parameters - } - - const ignoreSymlinks = folderQueries.every(folderQuery => { - const folderConfig = this._configurationService.getValue({ resource: folderQuery.folder }); - return !folderConfig.search.followSymlinks; - }); - - // TODO replace wth QueryBuilder - folderQueries.forEach(fq => { - fq.ignoreSymlinks = ignoreSymlinks; - }); - - const query: IFileQuery = { - folderQueries, - type: QueryType.File, - maxResults, - disregardExcludeSettings: excludePatternOrDisregardExcludes === false, - _reason: 'startFileSearch' - }; - if (typeof includePattern === 'string') { - query.includePattern = { [includePattern]: true }; - } - - if (typeof excludePatternOrDisregardExcludes === 'string') { - query.excludePattern = { [excludePatternOrDisregardExcludes]: true }; - } - - this._searchService.extendQuery(query); + const query = this._queryBuilder.file( + includeFolder ? [includeFolder] : workspace.folders.map(f => f.uri), + { + maxResults, + disregardExcludeSettings: (excludePatternOrDisregardExcludes === false) || undefined, + includePattern, + _reason: 'startFileSearch' + }); return this._searchService.fileSearch(query, token).then(result => { return result.results.map(m => m.resource); @@ -172,8 +142,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); - const queryBuilder = this._instantiationService.createInstance(QueryBuilder); - const query = queryBuilder.text(pattern, folders, options); + const query = this._queryBuilder.text(pattern, folders, options); query._reason = 'startTextSearch'; const onProgress = (p: ISearchProgressItem) => { diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 8d45afec201..394c74fdc2c 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -11,18 +11,16 @@ import { Event } from 'vs/base/common/event'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { keys, ResourceMap, values } from 'vs/base/common/map'; import { Schemas } from 'vs/base/common/network'; -import * as objects from 'vs/base/common/objects'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI as uri } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { getNextTickChannel } from 'vs/base/parts/ipc/node/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchConfiguration, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/platform/search/common/search'; +import { deserializeSearchError, FileMatch, ICachedSearchStats, IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, IProgress, ISearchComplete, ISearchEngineStats, ISearchProgressItem, ISearchQuery, ISearchResultProvider, ISearchService, ITextQuery, pathIncludedInQuery, QueryType, SearchError, SearchErrorCode, SearchProviderType } from 'vs/platform/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -46,7 +44,6 @@ export class SearchService extends Disposable implements ISearchService { @IEditorService private readonly editorService: IEditorService, @IEnvironmentService environmentService: IEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService, @IExtensionService private readonly extensionService: IExtensionService ) { @@ -71,22 +68,6 @@ export class SearchService extends Disposable implements ISearchService { }); } - extendQuery(query: IFileQuery): void { - const configuration = this.configurationService.getValue(); - - // Configuration: File Excludes - if (!query.disregardExcludeSettings) { - const fileExcludes = objects.deepClone(configuration && configuration.files && configuration.files.exclude); - if (fileExcludes) { - if (!query.excludePattern) { - query.excludePattern = fileExcludes; - } else { - objects.mixin(query.excludePattern, fileExcludes, false /* no overwrite */); - } - } - } - } - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { // Get local results from dirty/untitled const localResults = this.getLocalResults(query); From 9deb608cc6c152d091484aebd9efcbbb35206f73 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Feb 2019 04:55:31 +0000 Subject: [PATCH 0997/1066] Fix #67661 --- src/vs/workbench/parts/search/browser/searchWidget.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/search/browser/searchWidget.ts b/src/vs/workbench/parts/search/browser/searchWidget.ts index 76ff4706fb6..c40fdd82fb6 100644 --- a/src/vs/workbench/parts/search/browser/searchWidget.ts +++ b/src/vs/workbench/parts/search/browser/searchWidget.ts @@ -440,14 +440,16 @@ export class SearchWidget extends Widget { else if (keyboardEvent.equals(KeyCode.UpArrow)) { const ta = this.searchInput.domNode.querySelector('textarea'); - if (ta && ta.selectionStart > 0) { + const isMultiline = !!this.searchInput.getValue().match(/\n/); + if (ta && isMultiline && ta.selectionStart > 0) { keyboardEvent.stopPropagation(); } } else if (keyboardEvent.equals(KeyCode.DownArrow)) { const ta = this.searchInput.domNode.querySelector('textarea'); - if (ta && ta.selectionEnd < ta.value.length) { + const isMultiline = !!this.searchInput.getValue().match(/\n/); + if (ta && isMultiline && ta.selectionEnd < ta.value.length) { keyboardEvent.stopPropagation(); } } From d50594c1c69e82c671b88fd45314e2acead4fca2 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Mon, 4 Feb 2019 19:46:00 +0100 Subject: [PATCH 0998/1066] Fix build on macOS --- build/azure-pipelines/darwin/product-build-darwin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index cdd8dfb9c89..2427beb711e 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -21,9 +21,9 @@ steps: - script: | set -e + yarn gulp -- vscode-darwin-min VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ - yarn gulp -- vscode-darwin-min yarn gulp -- upload-vscode-sourcemaps displayName: Build From c91613496bd4f665a03ad2c475244bf9dc92a352 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 5 Feb 2019 09:42:35 +0100 Subject: [PATCH 0999/1066] fixes #67883 --- .../files/electron-browser/views/openEditorsView.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 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 398894eee0a..dedd674663a 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -39,7 +39,7 @@ import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/v import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; -import { DesktopDragAndDropData, ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { URI } from 'vs/base/common/uri'; const $ = dom.$; @@ -652,15 +652,14 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop group, () => group.focus(), index); - } else { - const elementsData = (data as ElementsDragAndDropData).elements; + if (data instanceof ElementsDragAndDropData) { + const elementsData = data.elements; elementsData.forEach((oe, offset) => { oe.group.moveEditor(oe.editor, group, { index: index + offset, preserveFocus: true }); }); this.editorGroupService.activateGroup(group); + } else { + this.dropHandler.handleDrop(originalEvent, () => group, () => group.focus(), index); } - } } From d1546d83f6aca8b604bf44f5c9086dfd79fd34ff Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 5 Feb 2019 11:09:56 +0100 Subject: [PATCH 1000/1066] :lipstick: --- src/vs/platform/list/browser/listService.ts | 60 +++++++++++++-------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 0966123e57d..0a26a9aebf4 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -232,27 +232,6 @@ function getSharedListStyleSheet(): HTMLStyleElement { return sharedListStyleSheet; } -let sharedTreeStyleSheet: HTMLStyleElement; -function getSharedTreeStyleSheet(): HTMLStyleElement { - if (!sharedTreeStyleSheet) { - sharedTreeStyleSheet = createStyleSheet(); - } - - return sharedTreeStyleSheet; -} - -function handleTreeController(configuration: ITreeConfiguration, instantiationService: IInstantiationService): ITreeConfiguration { - if (!configuration.controller) { - configuration.controller = instantiationService.createInstance(WorkbenchTreeController, {}); - } - - if (!configuration.styler) { - configuration.styler = new DefaultTreestyler(getSharedTreeStyleSheet()); - } - - return configuration; -} - export class WorkbenchList extends List { readonly contextKeyService: IContextKeyService; @@ -408,6 +387,36 @@ export class WorkbenchPagedList extends PagedList { } } +/** + * @deprecated + */ +let sharedTreeStyleSheet: HTMLStyleElement; +function getSharedTreeStyleSheet(): HTMLStyleElement { + if (!sharedTreeStyleSheet) { + sharedTreeStyleSheet = createStyleSheet(); + } + + return sharedTreeStyleSheet; +} + +/** + * @deprecated + */ +function handleTreeController(configuration: ITreeConfiguration, instantiationService: IInstantiationService): ITreeConfiguration { + if (!configuration.controller) { + configuration.controller = instantiationService.createInstance(WorkbenchTreeController, {}); + } + + if (!configuration.styler) { + configuration.styler = new DefaultTreestyler(getSharedTreeStyleSheet()); + } + + return configuration; +} + +/** + * @deprecated + */ export class WorkbenchTree extends Tree { readonly contextKeyService: IContextKeyService; @@ -502,6 +511,9 @@ export class WorkbenchTree extends Tree { } } +/** + * @deprecated + */ function massageControllerOptions(options: IControllerOptions): IControllerOptions { if (typeof options.keyboardSupport !== 'boolean') { options.keyboardSupport = false; @@ -514,6 +526,9 @@ function massageControllerOptions(options: IControllerOptions): IControllerOptio return options; } +/** + * @deprecated + */ export class WorkbenchTreeController extends DefaultController { protected disposables: IDisposable[] = []; @@ -559,6 +574,9 @@ export interface IResourceResultsNavigationOptions { openOnFocus: boolean; } +/** + * @deprecated + */ export class TreeResourceNavigator extends Disposable { private readonly _openResource = new Emitter(); From 4aa198c4eeab64c1747530c9e3793f0078c69fe5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 5 Feb 2019 11:10:04 +0100 Subject: [PATCH 1001/1066] add logging for resolving authority on main side --- src/vs/code/electron-main/app.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 8a2f82f82ab..8c39f0d1e50 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -701,14 +701,17 @@ export class CodeApplication extends Disposable { const resolvedAuthorities = new Map(); ipc.on('vscode:remoteAuthorityResolved', (event: any, data: ResolvedAuthority) => { + this.logService.info('Receieved resolved authority', data.authority); resolvedAuthorities.set(data.authority, data); }); const resolveAuthority = (authority: string): ResolvedAuthority | null => { + this.logService.info('Resolving authority', authority); if (authority.indexOf('+') >= 0) { if (resolvedAuthorities.has(authority)) { return resolvedAuthorities.get(authority) || null; } + this.logService.info('Didnot find resolved authority for', authority); return null; } else { const [host, strPort] = authority.split(':'); From cf0fa0861ce5994dbf72af8cd49e830c8510be3a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 5 Feb 2019 12:36:34 +0100 Subject: [PATCH 1002/1066] fix #67813 --- .../parts/preferences/browser/keybindingsEditor.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts index 8d358e31f2c..71f261fc2f6 100644 --- a/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/keybindingsEditor.ts @@ -35,7 +35,7 @@ import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes'; -import { listHighlightForeground, badgeBackground, contrastBorder, badgeForeground } from 'vs/platform/theme/common/colorRegistry'; +import { listHighlightForeground, badgeBackground, contrastBorder, badgeForeground, listActiveSelectionForeground } from 'vs/platform/theme/common/colorRegistry'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; @@ -940,4 +940,8 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { if (listHighlightForegroundColor) { collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`); } + const listFocusAndSelectionForegroundColor = theme.getColor(listActiveSelectionForeground); + if (listFocusAndSelectionForegroundColor) { + collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list:focus .monaco-list-row.selected.focused > .column .monaco-keybinding-key { color: ${listFocusAndSelectionForegroundColor}; }`); + } }); From beabfb15c801546c0e0f3079b8358edf2e237dad Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 5 Feb 2019 12:40:28 +0100 Subject: [PATCH 1003/1066] bring back `Promise.finally` change --- .../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 eec60635669..ab6982abed3 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'; @@ -306,7 +306,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( @@ -464,7 +464,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)); })); } @@ -475,7 +475,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 30afe8898a678b6ddfe2b9766f1f6ba617ffbda8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 5 Feb 2019 12:52:01 +0100 Subject: [PATCH 1004/1066] debt - smarter initial commands picker --- .../quickopen/browser/commandsHandler.ts | 171 ++++++++++-------- 1 file changed, 91 insertions(+), 80 deletions(-) diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index c1c6e03e2c3..d86c5ac4cfd 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -31,6 +31,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { Disposable } from 'vs/base/common/lifecycle'; +import { timeout } from 'vs/base/common/async'; export const ALL_COMMANDS_PREFIX = '>'; @@ -376,6 +377,7 @@ export class CommandsHandler extends QuickOpenHandler { private commandHistoryEnabled: boolean; private commandsHistory: CommandsHistory; + private extensionsRegistered: boolean; constructor( @IEditorService private readonly editorService: IEditorService, @@ -389,6 +391,8 @@ export class CommandsHandler extends QuickOpenHandler { this.commandsHistory = this.instantiationService.createInstance(CommandsHistory); + this.extensionService.whenInstalledExtensionsRegistered().then(() => this.extensionsRegistered = true); + this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()); this.updateConfiguration(); } @@ -398,89 +402,96 @@ export class CommandsHandler extends QuickOpenHandler { } getResults(searchValue: string, token: CancellationToken): Promise { + if (this.extensionsRegistered) { + return this.doGetResults(searchValue, token); + } - // wait for extensions being registered to cover all commands - // also from extensions - return this.extensionService.whenInstalledExtensionsRegistered().then(() => { - if (token.isCancellationRequested) { - return new QuickOpenModel([]); + // If extensions are not yet registered, we wait for a little moment to give them + // a chance to register so that the complete set of commands shows up as result + // We do not want to delay functionality beyond that time though to keep the commands + // functional. + return Promise.race([timeout(800), this.extensionService.whenInstalledExtensionsRegistered().then(() => undefined)]).then(() => this.doGetResults(searchValue, token)); + } + + private doGetResults(searchValue: string, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return Promise.resolve(new QuickOpenModel([])); + } + + searchValue = searchValue.trim(); + + // Remember as last command palette input + lastCommandPaletteInput = searchValue; + + // Editor Actions + const activeTextEditorWidget = this.editorService.activeTextEditorWidget; + let editorActions: IEditorAction[] = []; + if (activeTextEditorWidget && types.isFunction(activeTextEditorWidget.getSupportedActions)) { + editorActions = activeTextEditorWidget.getSupportedActions(); + } + + const editorEntries = this.editorActionsToEntries(editorActions, searchValue); + + // Other Actions + const menu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.CommandPalette, accessor.get(IContextKeyService))); + const menuActions = menu.getActions().reduce((r, [, actions]) => [...r, ...actions], []).filter(action => action instanceof MenuItemAction) as MenuItemAction[]; + const commandEntries = this.menuItemActionsToEntries(menuActions, searchValue); + menu.dispose(); + + // Concat + let entries = [...editorEntries, ...commandEntries]; + + // Remove duplicates + entries = arrays.distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`); + + // Handle label clashes + const commandLabels = new Set(); + entries.forEach(entry => { + const commandLabel = `${entry.getLabel()}${entry.getGroupLabel()}`; + if (commandLabels.has(commandLabel)) { + entry.setDescription(entry.getCommandId()); + } else { + commandLabels.add(commandLabel); } - - searchValue = searchValue.trim(); - - // Remember as last command palette input - lastCommandPaletteInput = searchValue; - - // Editor Actions - const activeTextEditorWidget = this.editorService.activeTextEditorWidget; - let editorActions: IEditorAction[] = []; - if (activeTextEditorWidget && types.isFunction(activeTextEditorWidget.getSupportedActions)) { - editorActions = activeTextEditorWidget.getSupportedActions(); - } - - const editorEntries = this.editorActionsToEntries(editorActions, searchValue); - - // Other Actions - const menu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.CommandPalette, accessor.get(IContextKeyService))); - const menuActions = menu.getActions().reduce((r, [, actions]) => [...r, ...actions], []).filter(action => action instanceof MenuItemAction) as MenuItemAction[]; - const commandEntries = this.menuItemActionsToEntries(menuActions, searchValue); - menu.dispose(); - - // Concat - let entries = [...editorEntries, ...commandEntries]; - - // Remove duplicates - entries = arrays.distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`); - - // Handle label clashes - const commandLabels = new Set(); - entries.forEach(entry => { - const commandLabel = `${entry.getLabel()}${entry.getGroupLabel()}`; - if (commandLabels.has(commandLabel)) { - entry.setDescription(entry.getCommandId()); - } else { - commandLabels.add(commandLabel); - } - }); - - // Sort by MRU order and fallback to name otherwie - entries = entries.sort((elementA, elementB) => { - const counterA = this.commandsHistory.peek(elementA.getCommandId()); - const counterB = this.commandsHistory.peek(elementB.getCommandId()); - - if (counterA && counterB) { - return counterA > counterB ? -1 : 1; // use more recently used command before older - } - - if (counterA) { - return -1; // first command was used, so it wins over the non used one - } - - if (counterB) { - return 1; // other command was used so it wins over the command - } - - // both commands were never used, so we sort by name - return elementA.getSortLabel().localeCompare(elementB.getSortLabel()); - }); - - // Introduce group marker border between recently used and others - // only if we have recently used commands in the result set - const firstEntry = entries[0]; - if (firstEntry && this.commandsHistory.peek(firstEntry.getCommandId())) { - firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used")); - for (let i = 1; i < entries.length; i++) { - const entry = entries[i]; - if (!this.commandsHistory.peek(entry.getCommandId())) { - entry.setShowBorder(true); - entry.setGroupLabel(nls.localize('morecCommands', "other commands")); - break; - } - } - } - - return new QuickOpenModel(entries); }); + + // Sort by MRU order and fallback to name otherwie + entries = entries.sort((elementA, elementB) => { + const counterA = this.commandsHistory.peek(elementA.getCommandId()); + const counterB = this.commandsHistory.peek(elementB.getCommandId()); + + if (counterA && counterB) { + return counterA > counterB ? -1 : 1; // use more recently used command before older + } + + if (counterA) { + return -1; // first command was used, so it wins over the non used one + } + + if (counterB) { + return 1; // other command was used so it wins over the command + } + + // both commands were never used, so we sort by name + return elementA.getSortLabel().localeCompare(elementB.getSortLabel()); + }); + + // Introduce group marker border between recently used and others + // only if we have recently used commands in the result set + const firstEntry = entries[0]; + if (firstEntry && this.commandsHistory.peek(firstEntry.getCommandId())) { + firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used")); + for (let i = 1; i < entries.length; i++) { + const entry = entries[i]; + if (!this.commandsHistory.peek(entry.getCommandId())) { + entry.setShowBorder(true); + entry.setGroupLabel(nls.localize('morecCommands', "other commands")); + break; + } + } + } + + return Promise.resolve(new QuickOpenModel(entries)); } private editorActionsToEntries(actions: IEditorAction[], searchValue: string): EditorActionCommandEntry[] { From 98eb895141768bfa4e9198832b2453a294ba4ecf Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Feb 2019 04:44:13 -0800 Subject: [PATCH 1005/1066] Implement onDidChangeTerminalDimensions Fixes #55718 --- .../src/singlefolder-tests/window.test.ts | 41 ++++++++++++++++++- src/vs/vscode.proposed.d.ts | 6 ++- .../mainThreadTerminalService.ts | 11 +++-- src/vs/workbench/api/node/extHost.api.impl.ts | 3 ++ src/vs/workbench/api/node/extHost.protocol.ts | 2 +- .../api/node/extHostTerminalService.ts | 36 +++++++++++++++- .../electron-browser/terminalInstance.ts | 23 +++++++---- 7 files changed, 103 insertions(+), 19 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index 707508b723c..a08d5d049d5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, Terminal } from 'vscode'; +import { workspace, window, commands, ViewColumn, TextEditorViewColumnChangeEvent, Uri, Selection, Position, CancellationTokenSource, TextEditorSelectionChangeKind, Terminal, TerminalDimensionsChangeEvent } from 'vscode'; import { join } from 'path'; import { closeAllEditors, pathEquals, createRandomFile } from '../utils'; @@ -695,6 +695,45 @@ suite('window namespace tests', () => { const terminal = window.createTerminal(); terminal.show(); }); + + test('onDidChangeTerminalDimensions should fire when new terminals are created', (done) => { + const reg1 = window.onDidChangeTerminalDimensions((event: TerminalDimensionsChangeEvent) => { + assert.equal(event.terminal, terminal1); + assert.equal(typeof event.dimensions.columns, 'number'); + assert.equal(typeof event.dimensions.rows, 'number'); + assert.ok(event.dimensions.columns > 0); + assert.ok(event.dimensions.rows > 0); + reg1.dispose(); + let terminal2: Terminal; + const reg2 = window.onDidOpenTerminal((newTerminal) => { + // THis is guarentees to fire before dimensions change event + if (newTerminal !== terminal1) { + terminal2 = newTerminal; + reg2.dispose(); + } + }); + let firstCalled = false; + let secondCalled = false; + const reg3 = window.onDidChangeTerminalDimensions((event: TerminalDimensionsChangeEvent) => { + if (event.terminal === terminal1) { + // The original terminal should fire dimension change after a split + firstCalled = true; + } else if (event.terminal !== terminal1) { + // The new split terminal should fire dimension change + secondCalled = true; + } + if (firstCalled && secondCalled) { + terminal1.dispose(); + terminal2.dispose(); + reg3.dispose(); + done(); + } + }); + commands.executeCommand('workbench.action.terminal.split'); + }); + const terminal1 = window.createTerminal({ name: 'test' }); + terminal1.show(); + }); }); }); diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 315b263c9f5..6192f6b1bbc 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -911,9 +911,11 @@ declare module 'vscode' { export interface Terminal { /** - * The current dimensions of the terminal. + * The current dimensions of the terminal. This will be `undefined` immediately after the + * terminal is created as the dimensions are not known until shortly after the terminal is + * created. */ - readonly dimensions: TerminalDimensions; + readonly dimensions: TerminalDimensions | undefined; /** * Fires when the terminal's pty slave pseudo-device is written to. In other words, this diff --git a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts index 4a846cf2952..bdc0b212563 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTerminalService.ts @@ -28,7 +28,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape // 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 // to register non-extension API terminals in the extension host. - setTimeout(() => this._onTerminalOpened(instance), EXT_HOST_CREATION_DELAY); + setTimeout(() => { + this._onTerminalOpened(instance); + this._onInstanceDimensionsChanged(instance); + }, EXT_HOST_CREATION_DELAY); })); this._toDispose.push(terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance))); this._toDispose.push(terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance))); @@ -196,11 +199,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } private _onInstanceDimensionsChanged(instance: ITerminalInstance): void { - // Only send the dimensions if the terminal is a renderer only as there is no API to access - // dimensions on a plain Terminal. - if (instance.shellLaunchConfig.isRendererOnly) { - this._proxy.$acceptTerminalRendererDimensions(instance.id, instance.cols, instance.rows); - } + this._proxy.$acceptTerminalDimensions(instance.id, instance.cols, instance.rows); } private _onTerminalRequestExtHostProcess(request: ITerminalProcessExtHostRequest): void { diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index f6d2526d4b4..7373f1f92d4 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -424,6 +424,9 @@ export function createApiFactory( onDidChangeActiveTerminal(listener, thisArg?, disposables?) { return extHostTerminalService.onDidChangeActiveTerminal(listener, thisArg, disposables); }, + onDidChangeTerminalDimensions(listener, thisArg?, disposables?) { + return extHostTerminalService.onDidChangeTerminalDimensions(listener, thisArg, disposables); + }, get state() { return extHostWindow.state; }, diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 85e68f5080d..93bf62e21a9 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -935,7 +935,7 @@ export interface ExtHostTerminalServiceShape { $acceptTerminalProcessData(id: number, data: string): void; $acceptTerminalRendererInput(id: number, data: string): void; $acceptTerminalTitleChange(id: number, name: string): void; - $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void; + $acceptTerminalDimensions(id: number, cols: number, rows: number): void; $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: URI, cols: number, rows: number): void; $acceptProcessInput(id: number, data: string): void; $acceptProcessResize(id: number, cols: number, rows: number): void; diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 1136debe9a1..005f6b766a8 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -78,6 +78,8 @@ export class BaseExtHostTerminal { export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Terminal { private _pidPromise: Promise; private _pidPromiseComplete: (value: number) => any; + private _cols: number | undefined; + private _rows: number | undefined; private readonly _onData = new Emitter(); public get onDidWriteData(): Event { @@ -126,6 +128,26 @@ export class ExtHostTerminal extends BaseExtHostTerminal implements vscode.Termi this._name = name; } + public get dimensions(): vscode.TerminalDimensions | undefined { + if (this._cols === undefined && this._rows === undefined) { + return undefined; + } + return { + columns: this._cols, + rows: this._rows + }; + } + + public setDimensions(cols: number, rows: number): boolean { + if (cols === this._cols && rows === this._rows) { + // Nothing changed + return false; + } + this._cols = cols; + this._rows = rows; + return true; + } + public get processId(): Promise { return this._pidPromise; } @@ -258,6 +280,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { public get onDidOpenTerminal(): Event { return this._onDidOpenTerminal && this._onDidOpenTerminal.event; } private readonly _onDidChangeActiveTerminal: Emitter = new Emitter(); public get onDidChangeActiveTerminal(): Event { return this._onDidChangeActiveTerminal && this._onDidChangeActiveTerminal.event; } + private readonly _onDidChangeTerminalDimensions: Emitter = new Emitter(); + public get onDidChangeTerminalDimensions(): Event { return this._onDidChangeTerminalDimensions && this._onDidChangeTerminalDimensions.event; } constructor( mainContext: IMainContext, @@ -319,7 +343,17 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { }); } - public $acceptTerminalRendererDimensions(id: number, cols: number, rows: number): void { + public async $acceptTerminalDimensions(id: number, cols: number, rows: number): Promise { + const terminal = this._getTerminalById(id); + if (terminal) { + if (terminal.setDimensions(cols, rows)) { + this._onDidChangeTerminalDimensions.fire({ + terminal: terminal, + dimensions: terminal.dimensions + }); + } + } + // When a terminal's dimensions change, a renderer's _maximum_ dimensions change const renderer = this._getTerminalRendererById(id); if (renderer) { renderer._setMaximumDimensions(cols, rows); diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index b19e38fcae5..15cc76a4fc3 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -190,8 +190,18 @@ export class TerminalInstance implements ITerminalInstance { public disableLayout: boolean; public get id(): number { return this._id; } - public get cols(): number { return this._cols; } - public get rows(): number { return this._rows; } + public get cols(): number { + if (this._dimensionsOverride && this._dimensionsOverride.cols) { + return Math.min(Math.max(this._dimensionsOverride.cols, 2), this._cols); + } + return this._cols; + } + public get rows(): number { + if (this._dimensionsOverride && this._dimensionsOverride.rows) { + return Math.min(Math.max(this._dimensionsOverride.rows, 2), this._rows); + } + return this._rows; + } // TODO: Ideally processId would be merged into processReady public get processId(): number | undefined { return this._processManager ? this._processManager.shellProcessId : undefined; } // TODO: How does this work with detached processes? @@ -1173,6 +1183,7 @@ export class TerminalInstance implements ITerminalInstance { return; } + const terminalWidth = this._evaluateColsAndRows(dimension.width, dimension.height); if (!terminalWidth) { return; @@ -1187,12 +1198,8 @@ export class TerminalInstance implements ITerminalInstance { @debounce(50) private _resize(): void { - let cols = this._cols; - let rows = this._rows; - if (this._dimensionsOverride && this._dimensionsOverride.cols && this._dimensionsOverride.rows) { - cols = Math.min(Math.max(this._dimensionsOverride.cols, 2), cols); - rows = Math.min(Math.max(this._dimensionsOverride.rows, 2), rows); - } + let cols = this.cols; + let rows = this.rows; if (this._xterm) { // Only apply these settings when the terminal is visible so that From 7c8e79b5767ef29776d569b09dc530ec8b5e4630 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 10:03:46 +0100 Subject: [PATCH 1006/1066] Introduce gulpfile.compile --- build/gulpfile.compile.js | 20 ++++++++++++++++++++ build/gulpfile.extensions.js | 6 +++++- build/gulpfile.vscode.js | 9 +-------- 3 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 build/gulpfile.compile.js diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js new file mode 100644 index 00000000000..521a13a440d --- /dev/null +++ b/build/gulpfile.compile.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const util = require('./lib/util'); +const compilation = require('./lib/compilation'); +const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); + +// Full compile, including nls and inline sources in sourcemaps, for build +const compileClientBuildTask = util.task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true)); +compileClientBuildTask.displayName = 'compile-client-build'; + +// All Build +const compileBuildTask = util.task.parallel(compileClientBuildTask, compileExtensionsBuildTask); +compileBuildTask.displayName = 'compile-build'; + +exports.compileBuildTask = compileBuildTask; \ No newline at end of file diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 46fc1940bd5..af3278cde5e 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -143,4 +143,8 @@ const tasks = compilations.map(function (tsconfigFile) { gulp.task('compile-extensions', util.task.parallel(...tasks.map(t => t.compileTask))); gulp.task('watch-extensions', util.task.parallel(...tasks.map(t => t.watchTask))); -exports.compileExtensionsBuildTask = util.task.parallel(...tasks.map(t => t.compileBuildTask)); + +const compileExtensionsBuildTask = util.task.parallel(...tasks.map(t => t.compileBuildTask)); +compileExtensionsBuildTask.displayName = 'compile-extensions-build'; + +exports.compileExtensionsBuildTask = compileExtensionsBuildTask; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index dde330fb9ea..48df754d201 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -33,8 +33,7 @@ const deps = require('./dependencies'); const getElectronVersion = require('./lib/electron').getElectronVersion; const createAsar = require('./lib/asar').createAsar; const minimist = require('minimist'); -const compilation = require('./lib/compilation'); -const { compileExtensionsBuildTask } = require('./gulpfile.extensions'); +const { compileBuildTask } = require('./gulpfile.compile'); const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname)); // @ts-ignore @@ -88,12 +87,6 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -// Full compile, including nls and inline sources in sourcemaps, for build -const compileClientBuildTask = util.task.series(util.rimraf('out-build'), compilation.compileTask('src', 'out-build', true)); - -// All Build -const compileBuildTask = util.task.parallel(compileClientBuildTask, compileExtensionsBuildTask); - gulp.task('optimize-vscode', util.task.series( util.task.parallel(util.rimraf('out-vscode'), compileBuildTask), common.optimizeTask({ From 87e19ff7ae47d697f9bc2fd1dd7ce3521f0b8d1e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 10:08:08 +0100 Subject: [PATCH 1007/1066] Formatting --- build/gulpfile.vscode.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 48df754d201..9629e6dec1a 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -87,18 +87,23 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -gulp.task('optimize-vscode', util.task.series( - util.task.parallel(util.rimraf('out-vscode'), compileBuildTask), - common.optimizeTask({ - src: 'out-build', - entryPoints: vscodeEntryPoints, - otherSources: [], - resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), - header: BUNDLED_FILE_HEADER, - out: 'out-vscode', - bundleInfo: undefined - })) +gulp.task('optimize-vscode', + util.task.series( + util.task.parallel( + util.rimraf('out-vscode'), + compileBuildTask + ), + common.optimizeTask({ + src: 'out-build', + entryPoints: vscodeEntryPoints, + otherSources: [], + resources: vscodeResources, + loaderConfig: common.loaderConfig(nodeModules), + header: BUNDLED_FILE_HEADER, + out: 'out-vscode', + bundleInfo: undefined + }) + ) ); From e911796b8ac3d382b651012796d42d6ef041f088 Mon Sep 17 00:00:00 2001 From: Alexandr Fadeev Date: Tue, 5 Feb 2019 11:11:15 +0200 Subject: [PATCH 1008/1066] Makefile colorizing tests - handling variable expansion in a variable name in assignment: var-$(another-var) := value --- .../make/test/colorize-fixtures/makefile | 7 + .../make/test/colorize-results/makefile.json | 255 +++++++++++++++++- 2 files changed, 261 insertions(+), 1 deletion(-) diff --git a/extensions/make/test/colorize-fixtures/makefile b/extensions/make/test/colorize-fixtures/makefile index 65fa3497669..6fbe91275ff 100644 --- a/extensions/make/test/colorize-fixtures/makefile +++ b/extensions/make/test/colorize-fixtures/makefile @@ -75,3 +75,10 @@ var:=123 var!=echo val var:=val \ notvar=butval + +var-$(nested-var)=val + +# Spaces in a nested shell will hurt a colorizing of variable, +# but not so much. +var-$(shell printf 2) := val2 +$(info Should be 'val2' here: $(var-2)) diff --git a/extensions/make/test/colorize-results/makefile.json b/extensions/make/test/colorize-results/makefile.json index ec834fa59a5..cd9a6c46e53 100644 --- a/extensions/make/test/colorize-results/makefile.json +++ b/extensions/make/test/colorize-results/makefile.json @@ -3012,5 +3012,258 @@ "light_vs": "default: #000000", "hc_black": "default: #FFFFFF" } + }, + { + "c": "var-", + "t": "source.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": "$(", + "t": "source.makefile variable.other.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "nested-var", + "t": "source.makefile variable.other.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile variable.other.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "=", + "t": "source.makefile punctuation.separator.key-value.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "val", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "#", + "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " Spaces in a nested shell will hurt a colorizing of variable,", + "t": "source.makefile comment.line.number-sign.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "#", + "t": "source.makefile comment.line.number-sign.makefile punctuation.definition.comment.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": " but not so much.", + "t": "source.makefile comment.line.number-sign.makefile", + "r": { + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668" + } + }, + { + "c": "var-", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "shell", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.shell.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " printf 2", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": " := val2", + "t": "source.makefile", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "info", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile support.function.info.makefile", + "r": { + "dark_plus": "support.function: #DCDCAA", + "light_plus": "support.function: #795E26", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "support.function: #DCDCAA" + } + }, + { + "c": " Should be 'val2' here: ", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "$(", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": "var-2", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile variable.other.makefile", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "variable: #9CDCFE" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile meta.scope.function-call.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } + }, + { + "c": ")", + "t": "source.makefile string.interpolated.makefile punctuation.definition.variable.makefile", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178" + } } -] +] \ No newline at end of file From db91adfdf4cce70eb672a500c930fdeb68d9be89 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 13:58:43 +0100 Subject: [PATCH 1009/1066] Fix once again the macOS build --- build/azure-pipelines/darwin/product-build-darwin.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 2427beb711e..f28b5799dc3 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -21,6 +21,8 @@ steps: - script: | set -e + VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ + AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ yarn gulp -- vscode-darwin-min VSCODE_MIXIN_PASSWORD="$(VSCODE_MIXIN_PASSWORD)" \ AZURE_STORAGE_ACCESS_KEY="$(AZURE_STORAGE_ACCESS_KEY)" \ From 15179bead4e473303bd1654c5d12c959c3549b60 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 5 Feb 2019 15:46:10 +0100 Subject: [PATCH 1010/1066] align datatree with asyncdatatree --- src/vs/base/browser/ui/tree/dataTree.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 4cc1d74e6b7..99fcac4edb5 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -17,7 +17,7 @@ export interface IDataTreeOptions extends IAbstractTreeOp export interface IDataTreeViewState { readonly focus: string[]; readonly selection: string[]; - readonly collapsed: string[]; + readonly expanded: string[]; } export class DataTree extends AbstractTree { @@ -70,7 +70,7 @@ export class DataTree extends AbstractTree extends AbstractTree 0) { const node = queue.shift()!; - if (node !== root && node.collapsed) { - collapsed.push(getId(node.element!)); + if (node !== root && node.collapsible && !node.collapsed) { + expanded.push(getId(node.element!)); } queue.push(...node.children); } - return { focus, selection, collapsed }; + return { focus, selection, expanded }; } } \ No newline at end of file From cf72cc410441b5a7fd398e7c0735ccbedba58727 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 5 Feb 2019 15:46:53 +0100 Subject: [PATCH 1011/1066] Fix #67821 --- .../markers/electron-browser/markersPanel.ts | 5 +- .../electron-browser/markersTreeViewer.ts | 47 ++++++++++++++++++- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts index c86ac4dca7c..0ab6213601a 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersPanel.ts @@ -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, MarkersViewModel } from 'vs/workbench/parts/markers/electron-browser/markersTreeViewer'; +import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, TreeElement, MarkersTreeAccessibilityProvider, MarkersViewModel, ResourceDragAndDrop } 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'; @@ -335,7 +335,8 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { { filter: this.filter, accessibilityProvider, - identityProvider + identityProvider, + dnd: new ResourceDragAndDrop(this.instantiationService) } ) as any as WorkbenchObjectTree; diff --git a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts index e7cb8ee2bbd..60902ef381b 100644 --- a/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts +++ b/src/vs/workbench/parts/markers/electron-browser/markersTreeViewer.ts @@ -19,9 +19,9 @@ import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/l 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 { dirname, basename } from 'vs/base/common/resources'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; -import { ITreeFilter, TreeVisibility, TreeFilterResult, ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { ITreeFilter, TreeVisibility, TreeFilterResult, ITreeRenderer, ITreeNode, ITreeDragAndDrop, ITreeDragOverReaction } from 'vs/base/browser/ui/tree/tree'; import { FilterOptions } from 'vs/workbench/parts/markers/electron-browser/markersFilterOptions'; import { IMatch } from 'vs/base/common/filters'; import { Event, Emitter } from 'vs/base/common/event'; @@ -30,6 +30,9 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; import { localize } from 'vs/nls'; +import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; export type TreeElement = ResourceMarkers | Marker | RelatedInformation; @@ -584,3 +587,43 @@ export class MarkersViewModel extends Disposable { } } + +export class ResourceDragAndDrop implements ITreeDragAndDrop { + constructor( + private instantiationService: IInstantiationService + ) { } + + onDragOver(data: IDragAndDropData, targetElement: TreeElement, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { + return false; + } + + getDragURI(element: TreeElement): string | null { + if (element instanceof ResourceMarkers) { + return element.resource.toString(); + } + return null; + } + + getDragLabel?(elements: TreeElement[]): string | undefined { + if (elements.length > 1) { + return String(elements.length); + } + const element = elements[0]; + return element instanceof ResourceMarkers ? basename(element.resource) : undefined; + } + + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { + const elements = (data as ElementsDragAndDropData).elements; + const resources: URI[] = elements + .filter(e => e instanceof ResourceMarkers) + .map((resourceMarker: ResourceMarkers) => resourceMarker.resource); + + if (resources.length) { + // Apply some datatransfer types to allow for dragging the element outside of the application + this.instantiationService.invokeFunction(fillResourceDataTransfers, resources, originalEvent); + } + } + + drop(data: IDragAndDropData, targetElement: TreeElement, targetIndex: number, originalEvent: DragEvent): void { + } +} From e50ef11ff189afa60ced0bfab7156d843f3881ba Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 5 Feb 2019 16:43:39 +0100 Subject: [PATCH 1012/1066] tree: refactor click handling for expansion --- src/vs/base/browser/ui/list/listWidget.ts | 40 +++++------ src/vs/base/browser/ui/tree/abstractTree.ts | 74 +++++++++++++-------- 2 files changed, 65 insertions(+), 49 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 53869580549..4724ca6224d 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -502,33 +502,29 @@ const DefaultOpenController: IOpenController = { } }; -class MouseController implements IDisposable { +export class MouseController implements IDisposable { private multipleSelectionSupport: boolean; readonly multipleSelectionController: IMultipleSelectionController; private openController: IOpenController; private disposables: IDisposable[] = []; - constructor( - private list: List, - private view: ListView, - options: IListOptions = {} - ) { - this.multipleSelectionSupport = !(options.multipleSelectionSupport === false); + constructor(protected list: List) { + this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false); if (this.multipleSelectionSupport) { - this.multipleSelectionController = options.multipleSelectionController || DefaultMultipleSelectionContoller; + this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionContoller; } - this.openController = options.openController || DefaultOpenController; + this.openController = list.options.openController || DefaultOpenController; - view.onMouseDown(this.onMouseDown, this, this.disposables); - view.onContextMenu(this.onContextMenu, this, this.disposables); - view.onMouseClick(this.onPointer, this, this.disposables); - view.onMouseDblClick(this.onDoubleClick, this, this.disposables); - view.onTouchStart(this.onMouseDown, this, this.disposables); - view.onTap(this.onPointer, this, this.disposables); - Gesture.addTarget(view.domNode); + list.onMouseDown(this.onMouseDown, this, this.disposables); + list.onContextMenu(this.onContextMenu, this, this.disposables); + list.onMouseClick(this.onPointer, this, this.disposables); + list.onMouseDblClick(this.onDoubleClick, this, this.disposables); + list.onTouchStart(this.onMouseDown, this, this.disposables); + list.onTap(this.onPointer, this, this.disposables); + Gesture.addTarget(list.getHTMLElement()); } private isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { @@ -553,16 +549,16 @@ class MouseController implements IDisposable { private onMouseDown(e: IListMouseEvent | IListTouchEvent): void { if (document.activeElement !== e.browserEvent.target) { - this.view.domNode.focus(); + this.list.domFocus(); } } - private onContextMenu(e: IListMouseEvent | IListTouchEvent): void { + private onContextMenu(e: IListContextMenuEvent): void { const focus = typeof e.index === 'undefined' ? [] : [e.index]; this.list.setFocus(focus, e.browserEvent); } - private onPointer(e: IListMouseEvent): void { + protected onPointer(e: IListMouseEvent): void { let reference = this.list.getFocus()[0]; const selection = this.list.getSelection(); reference = reference === undefined ? selection[0] : reference; @@ -1213,7 +1209,7 @@ export class List implements ISpliceable, IDisposable { } if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) { - this.mouseController = new MouseController(this, this.view, _options); + this.mouseController = this.createMouseController(_options); this.disposables.push(this.mouseController); } @@ -1227,6 +1223,10 @@ export class List implements ISpliceable, IDisposable { this.style(_options); } + protected createMouseController(options: IListOptions): MouseController { + return new MouseController(this); + } + updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { this._options = { ...this._options, ...optionsUpdate }; this._onDidUpdateOptions.fire(this._options); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 134044cfde3..3262608778c 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -5,7 +5,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget'; +import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; @@ -761,11 +761,47 @@ class Trait { } } +export class TreeNodeListMouseController extends MouseController> { + + constructor(list: TreeNodeList, private tree: AbstractTree) { + super(list); + } + + protected onPointer(e: IListMouseEvent>): void { + const node = e.element; + + if (!node) { + super.onPointer(e); + return; + } + + if (this.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.multipleSelectionController.isSelectionSingleChangeEvent(e)) { + super.onPointer(e); + return; + } + + if (!this.tree.options.openOnSingleClick && e.browserEvent.detail !== 2) { + super.onPointer(e); + return; + } + + const model = ((this.tree as any).model as ITreeModel); // internal + const location = model.getNodeLocation(node); + const recursive = e.browserEvent.altKey; + model.setCollapsed(location, undefined, recursive); + super.onPointer(e); + } +} + +interface ITreeNodeListOptions extends IListOptions> { + readonly tree: AbstractTree; +} + /** * We use this List subclass to restore selection and focus as nodes * get rendered in the list, possibly due to a node expand() call. */ -class TreeNodeList extends List> { +class TreeNodeList extends List> { constructor( container: HTMLElement, @@ -773,11 +809,15 @@ class TreeNodeList extends List> { renderers: IListRenderer[], private focusTrait: Trait, private selectionTrait: Trait, - options?: IListOptions> + options: ITreeNodeListOptions ) { super(container, virtualDelegate, renderers, options); } + protected createMouseController(options: ITreeNodeListOptions): MouseController> { + return new TreeNodeListMouseController(this, options.tree); + } + splice(start: number, deleteCount: number, elements: ITreeNode[] = []): void { super.splice(start, deleteCount, elements); @@ -826,7 +866,7 @@ class TreeNodeList extends List> { export abstract class AbstractTree implements IDisposable { - private view: TreeNodeList; + private view: TreeNodeList; private renderers: TreeRenderer[]; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; @@ -884,7 +924,7 @@ export abstract class AbstractTree implements IDisposable this.disposables.push(filter); } - this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, asListOptions(() => this.model, _options)); + this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, { ...asListOptions(() => this.model, _options), tree: this }); this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; @@ -920,9 +960,6 @@ export abstract class AbstractTree implements IDisposable }, null, this.disposables); } - this.view.onTap(this.reactOnMouseClick, this, this.disposables); - this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables); - if (_options.keyboardSupport !== false) { const onKeyDown = Event.chain(this.view.onKeyDown) .filter(e => !isInputElement(e.target as HTMLElement)) @@ -1164,27 +1201,6 @@ export abstract class AbstractTree implements IDisposable return this.view.length; } - private reactOnMouseClick(e: IListMouseEvent>): void { - const node = e.element; - - if (!node) { - return; - } - - if (this.view.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.view.multipleSelectionController.isSelectionSingleChangeEvent(e)) { - return; - } - - if (!this.openOnSingleClick && e.browserEvent.detail !== 2) { - return; - } - - const location = this.model.getNodeLocation(node); - const recursive = e.browserEvent.altKey; - - this.model.setCollapsed(location, undefined, recursive); - } - private onLeftArrow(e: StandardKeyboardEvent): void { e.preventDefault(); e.stopPropagation(); From 1dd559cddcfca9b81b3046981dd3743a78353e59 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Tue, 5 Feb 2019 17:13:23 +0100 Subject: [PATCH 1013/1066] tree.expandOnlyOnTwistieClick fixes #67909 --- src/vs/base/browser/ui/tree/abstractTree.ts | 30 ++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3262608778c..64e526e262b 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, createStyleSheet } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -697,6 +697,7 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly dnd?: ITreeDragAndDrop; readonly autoExpandSingleChildren?: boolean; readonly keyboardNavigationEventFilter?: IKeyboardNavigationEventFilter; + readonly expandOnlyOnTwistieClick?: boolean; } /** @@ -761,7 +762,7 @@ class Trait { } } -export class TreeNodeListMouseController extends MouseController> { +class TreeNodeListMouseController extends MouseController> { constructor(list: TreeNodeList, private tree: AbstractTree) { super(list); @@ -771,24 +772,32 @@ export class TreeNodeListMouseController extends MouseCont const node = e.element; if (!node) { - super.onPointer(e); - return; + return super.onPointer(e); } if (this.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.multipleSelectionController.isSelectionSingleChangeEvent(e)) { - super.onPointer(e); - return; + return super.onPointer(e); } - if (!this.tree.options.openOnSingleClick && e.browserEvent.detail !== 2) { - super.onPointer(e); - return; + if (!this.tree.openOnSingleClick && e.browserEvent.detail !== 2) { + return super.onPointer(e); + } + + const onTwistie = hasClass(e.browserEvent.target as HTMLElement, 'monaco-tl-twistie'); + + if (this.tree.expandOnlyOnTwistieClick && !onTwistie) { + return super.onPointer(e); } const model = ((this.tree as any).model as ITreeModel); // internal const location = model.getNodeLocation(node); const recursive = e.browserEvent.altKey; model.setCollapsed(location, undefined, recursive); + + if (this.tree.expandOnlyOnTwistieClick && onTwistie) { + return; + } + super.onPointer(e); } } @@ -900,7 +909,10 @@ export abstract class AbstractTree implements IDisposable readonly onWillRefilter: Event = this._onWillRefilter.event; get filterOnType(): boolean { return !!this._options.filterOnType; } + + // Options TODO@joao expose options only, not Optional<> get openOnSingleClick(): boolean { return typeof this._options.openOnSingleClick === 'undefined' ? true : this._options.openOnSingleClick; } + get expandOnlyOnTwistieClick(): boolean { return typeof this._options.expandOnlyOnTwistieClick === 'undefined' ? false : this._options.expandOnlyOnTwistieClick; } get onDidDispose(): Event { return this.view.onDidDispose; } From 3d3844b8448bff9946825e661796306511be939d Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 18:08:41 +0100 Subject: [PATCH 1014/1066] Adopt new gulp tasks (#67863) --- build/gulpfile.vscode.js | 115 ++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 9629e6dec1a..c5887cde3f7 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -87,36 +87,46 @@ const BUNDLED_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -gulp.task('optimize-vscode', - util.task.series( - util.task.parallel( - util.rimraf('out-vscode'), - compileBuildTask - ), - common.optimizeTask({ - src: 'out-build', - entryPoints: vscodeEntryPoints, - otherSources: [], - resources: vscodeResources, - loaderConfig: common.loaderConfig(nodeModules), - header: BUNDLED_FILE_HEADER, - out: 'out-vscode', - bundleInfo: undefined - }) - ) +const optimizeVSCodeTask = util.task.series( + util.task.parallel( + util.rimraf('out-vscode'), + compileBuildTask + ), + common.optimizeTask({ + src: 'out-build', + entryPoints: vscodeEntryPoints, + otherSources: [], + resources: vscodeResources, + loaderConfig: common.loaderConfig(nodeModules), + header: BUNDLED_FILE_HEADER, + out: 'out-vscode', + bundleInfo: undefined + }) ); +optimizeVSCodeTask.displayName = 'optimize-vscode'; +gulp.task(optimizeVSCodeTask.displayName, optimizeVSCodeTask); -gulp.task('optimize-index-js', ['optimize-vscode'], () => { - const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); - const contents = fs.readFileSync(fullpath).toString(); - const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); - fs.writeFileSync(fullpath, newContents); -}); +const optimizeIndexJSTask = util.task.series( + optimizeVSCodeTask, + () => { + const fullpath = path.join(process.cwd(), 'out-vscode/bootstrap-window.js'); + const contents = fs.readFileSync(fullpath).toString(); + const newContents = contents.replace('[/*BUILD->INSERT_NODE_MODULES*/]', JSON.stringify(nodeModules)); + fs.writeFileSync(fullpath, newContents); + } +); +optimizeIndexJSTask.displayName = 'optimize-index-js'; const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; -gulp.task('clean-minified-vscode', util.rimraf('out-vscode-min')); -gulp.task('minify-vscode', ['clean-minified-vscode', 'optimize-index-js'], common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`)); +const minifyVSCodeTask = util.task.series( + util.task.parallel( + util.rimraf('out-vscode-min'), + optimizeIndexJSTask + ), + common.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) +); +minifyVSCodeTask.displayName = 'minify-vscode'; // Package @@ -246,14 +256,14 @@ function computeChecksum(filename) { return hash; } -function packageTask(platform, arch, opts) { +function packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) { opts = opts || {}; - const destination = path.join(path.dirname(root), 'VSCode') + (platform ? '-' + platform : '') + (arch ? '-' + arch : ''); + const destination = path.join(path.dirname(root), destinationFolderName); platform = platform || process.platform; return () => { - const out = opts.minified ? 'out-vscode-min' : 'out-vscode'; + const out = sourceFolderName; const checksums = computeChecksums(out, [ 'vs/workbench/workbench.main.js', @@ -430,29 +440,36 @@ function packageTask(platform, arch, opts) { const buildRoot = path.dirname(root); -gulp.task('clean-vscode-win32-ia32', util.rimraf(path.join(buildRoot, 'VSCode-win32-ia32'))); -gulp.task('clean-vscode-win32-x64', util.rimraf(path.join(buildRoot, 'VSCode-win32-x64'))); -gulp.task('clean-vscode-darwin', util.rimraf(path.join(buildRoot, 'VSCode-darwin'))); -gulp.task('clean-vscode-linux-ia32', util.rimraf(path.join(buildRoot, 'VSCode-linux-ia32'))); -gulp.task('clean-vscode-linux-x64', util.rimraf(path.join(buildRoot, 'VSCode-linux-x64'))); -gulp.task('clean-vscode-linux-arm', util.rimraf(path.join(buildRoot, 'VSCode-linux-arm'))); -gulp.task('clean-vscode-linux-arm64', util.rimraf(path.join(buildRoot, 'VSCode-linux-arm64'))); +const BUILD_TARGETS = [ + { platform: 'win32', arch: 'ia32' }, + { platform: 'win32', arch: 'x64' }, + { platform: 'darwin', arch: null, opts: { stats: true } }, + { platform: 'linux', arch: 'ia32' }, + { platform: 'linux', arch: 'x64' }, + { platform: 'linux', arch: 'arm' }, + { platform: 'linux', arch: 'arm64' }, +]; +BUILD_TARGETS.forEach(buildTarget => { + const dashed = (str) => (str ? `-${str}` : ``); + const platform = buildTarget.platform; + const arch = buildTarget.arch; + const opts = buildTarget.opts; -gulp.task('vscode-win32-ia32', ['optimize-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32')); -gulp.task('vscode-win32-x64', ['optimize-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64')); -gulp.task('vscode-darwin', ['optimize-vscode', 'clean-vscode-darwin'], packageTask('darwin', null, { stats: true })); -gulp.task('vscode-linux-ia32', ['optimize-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32')); -gulp.task('vscode-linux-x64', ['optimize-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64')); -gulp.task('vscode-linux-arm', ['optimize-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm')); -gulp.task('vscode-linux-arm64', ['optimize-vscode', 'clean-vscode-linux-arm64'], packageTask('linux', 'arm64')); + ['', 'min'].forEach(minified => { + const sourceFolderName = `out-vscode${dashed(minified)}`; + const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`; -gulp.task('vscode-win32-ia32-min', ['minify-vscode', 'clean-vscode-win32-ia32'], packageTask('win32', 'ia32', { minified: true })); -gulp.task('vscode-win32-x64-min', ['minify-vscode', 'clean-vscode-win32-x64'], packageTask('win32', 'x64', { minified: true })); -gulp.task('vscode-darwin-min', ['minify-vscode', 'clean-vscode-darwin'], packageTask('darwin', null, { minified: true, stats: true })); -gulp.task('vscode-linux-ia32-min', ['minify-vscode', 'clean-vscode-linux-ia32'], packageTask('linux', 'ia32', { minified: true })); -gulp.task('vscode-linux-x64-min', ['minify-vscode', 'clean-vscode-linux-x64'], packageTask('linux', 'x64', { minified: true })); -gulp.task('vscode-linux-arm-min', ['minify-vscode', 'clean-vscode-linux-arm'], packageTask('linux', 'arm', { minified: true })); -gulp.task('vscode-linux-arm64-min', ['minify-vscode', 'clean-vscode-linux-arm64'], packageTask('linux', 'arm64', { minified: true })); + const vscodeTask = util.task.series( + util.task.parallel( + minified ? minifyVSCodeTask : optimizeVSCodeTask, + util.rimraf(path.join(buildRoot, destinationFolderName)) + ), + packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) + ); + vscodeTask.displayName = `vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`; + gulp.task(vscodeTask.displayName, vscodeTask); + }); +}); // Transifex Localizations From add9437982c04313d76a035a5064c0b034c6c97c Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 18:18:18 +0100 Subject: [PATCH 1015/1066] WorkbenchAsyncDataTree implements IThemable --- build/monaco/monaco.usage.recipe | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index e75b8585a2d..fad1e8ee724 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -10,6 +10,7 @@ import { CountBadge } from './vs/base/browser/ui/countBadge/countBadge'; import { SimpleWorkerClient, create as create1 } from './vs/base/common/worker/simpleWorker'; import { create as create2 } from './vs/editor/common/services/editorSimpleWorker'; import { QuickOpenWidget } from './vs/base/parts/quickopen/browser/quickOpenWidget'; +import { WorkbenchAsyncDataTree } from './vs/platform/list/browser/listService'; import { SyncDescriptor0, SyncDescriptor1, SyncDescriptor2, SyncDescriptor3, SyncDescriptor4, SyncDescriptor5, SyncDescriptor6, SyncDescriptor7, SyncDescriptor8 } from './vs/platform/instantiation/common/descriptors'; import { DiffNavigator } from './vs/editor/browser/widget/diffNavigator'; import * as editorAPI from './vs/editor/editor.api'; @@ -22,6 +23,7 @@ import * as editorAPI from './vs/editor/editor.api'; a = (b).getWorkspace; // IWorkspaceFolderProvider a = (b).style; // IThemable a = (b).style; // IThemable + a = (>b).style; // IThemable a = (b).userHome; // IUserHomeProvider a = (b).previous; // IDiffNavigator a = (>b).type; From c9352c4ca97ac480caec922a1c970f5c91e1884f Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 19:23:51 +0100 Subject: [PATCH 1016/1066] Adopt new style gulp tasks for the editor (#67863) --- build/gulpfile.editor.js | 125 +++++++++++++++++---------------------- build/lib/util.js | 23 ++++--- build/lib/util.ts | 26 +++++--- 3 files changed, 86 insertions(+), 88 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index e9bb8cddf52..eb59eb533e1 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -63,7 +63,7 @@ var BUNDLED_FILE_HEADER = [ const languages = i18n.defaultLanguages.concat([]); // i18n.defaultLanguages.concat(process.env.VSCODE_QUALITY !== 'stable' ? i18n.extraLanguages : []); -gulp.task('extract-editor-src', util.task.series(util.rimraf('out-editor-src'), function () { +const extractEditorSrcTask = function () { console.log(`If the build fails, consider tweaking shakeLevel below to a lower value.`); const apiusages = monacoapi.execute().usageContent; const extrausages = fs.readFileSync(path.join(root, 'build', 'monaco', 'monaco.usage.recipe')).toString(); @@ -98,14 +98,13 @@ gulp.task('extract-editor-src', util.task.series(util.rimraf('out-editor-src'), importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, destRoot: path.join(root, 'out-editor-src') }); -})); +}; +extractEditorSrcTask.displayName = 'extract-editor-src'; -// Full compile, including nls and inline sources in sourcemaps, for build -gulp.task('clean-editor-build', util.rimraf('out-editor-build')); -gulp.task('compile-editor-build', ['clean-editor-build', 'extract-editor-src'], compilation.compileTask('out-editor-src', 'out-editor-build', true)); +const compileEditorAMDTask = compilation.compileTask('out-editor-src', 'out-editor-build', true); +compileEditorAMDTask.displayName = 'compile-editor-amd'; -gulp.task('clean-optimized-editor', util.rimraf('out-editor')); -gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], common.optimizeTask({ +const optimizeEditorAMDTask = common.optimizeTask({ src: 'out-editor-build', entryPoints: editorEntryPoints, otherSources: editorOtherSources, @@ -123,13 +122,13 @@ gulp.task('optimize-editor', ['clean-optimized-editor', 'compile-editor-build'], bundleInfo: true, out: 'out-editor', languages: languages -})); +}); +optimizeEditorAMDTask.displayName = 'optimize-editor-amd'; -gulp.task('clean-minified-editor', util.rimraf('out-editor-min')); -gulp.task('minify-editor', ['clean-minified-editor', 'optimize-editor'], common.minifyTask('out-editor')); +const minifyEditorAMDTask = common.minifyTask('out-editor'); +minifyEditorAMDTask.displayName = 'minify-editor-amd'; -gulp.task('clean-editor-esm', util.rimraf('out-editor-esm')); -gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro', 'extract-editor-src'], function () { +const createESMSourcesAndResourcesTask = function () { standalone.createESMSourcesAndResources2({ srcFolder: './out-editor-src', outFolder: './out-editor-esm', @@ -150,8 +149,10 @@ gulp.task('extract-editor-esm', ['clean-editor-esm', 'clean-editor-distro', 'ext 'vs/nls.mock.ts': 'vs/nls.ts' } }); -}); -gulp.task('compile-editor-esm', ['extract-editor-esm', 'clean-editor-distro'], function () { +}; +createESMSourcesAndResourcesTask.displayName = 'extract-editor-esm'; + +const compileEditorESMTask = function () { if (process.platform === 'win32') { const result = cp.spawnSync(`..\\node_modules\\.bin\\tsc.cmd`, { cwd: path.join(__dirname, '../out-editor-esm') @@ -165,7 +166,8 @@ gulp.task('compile-editor-esm', ['extract-editor-esm', 'clean-editor-distro'], f console.log(result.stdout.toString()); console.log(result.stderr.toString()); } -}); +}; +compileEditorESMTask.displayName = 'compile-editor-esm'; function toExternalDTS(contents) { let lines = contents.split('\n'); @@ -202,8 +204,16 @@ function toExternalDTS(contents) { return lines.join('\n'); } -gulp.task('clean-editor-distro', util.rimraf('out-monaco-editor-core')); -gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify-editor', 'optimize-editor'], function () { +function filterStream(testFunc) { + return es.through(function (data) { + if (!testFunc(data.relative)) { + return; + } + this.emit('data', data); + }); +} + +const finalEditorResourcesTask = function () { return es.merge( // other assets es.merge( @@ -280,61 +290,34 @@ gulp.task('editor-distro', ['clean-editor-distro', 'compile-editor-esm', 'minify return /\.js\.map$/.test(path); })).pipe(gulp.dest('out-monaco-editor-core/min-maps')) ); -}); - -gulp.task('analyze-editor-distro', function () { - // @ts-ignore - var bundleInfo = require('../out-editor/bundleInfo.json'); - var graph = bundleInfo.graph; - var bundles = bundleInfo.bundles; - - var inverseGraph = {}; - Object.keys(graph).forEach(function (module) { - var dependencies = graph[module]; - dependencies.forEach(function (dep) { - inverseGraph[dep] = inverseGraph[dep] || []; - inverseGraph[dep].push(module); - }); - }); - - var detailed = {}; - Object.keys(bundles).forEach(function (entryPoint) { - var included = bundles[entryPoint]; - var includedMap = {}; - included.forEach(function (included) { - includedMap[included] = true; - }); - - var explanation = []; - included.map(function (included) { - if (included.indexOf('!') >= 0) { - return; - } - - var reason = (inverseGraph[included] || []).filter(function (mod) { - return !!includedMap[mod]; - }); - explanation.push({ - module: included, - reason: reason - }); - }); - - detailed[entryPoint] = explanation; - }); - - console.log(JSON.stringify(detailed, null, '\t')); -}); - -function filterStream(testFunc) { - return es.through(function (data) { - if (!testFunc(data.relative)) { - return; - } - this.emit('data', data); - }); -} +}; +finalEditorResourcesTask.displayName = 'final-editor-resources'; +gulp.task('editor-distro', + util.task.series( + util.task.parallel( + util.rimraf('out-editor-src'), + util.rimraf('out-editor-build'), + util.rimraf('out-editor-esm'), + util.rimraf('out-monaco-editor-core'), + util.rimraf('out-editor'), + util.rimraf('out-editor-min') + ), + extractEditorSrcTask, + util.task.parallel( + util.task.series( + compileEditorAMDTask, + optimizeEditorAMDTask, + minifyEditorAMDTask + ), + util.task.series( + createESMSourcesAndResourcesTask, + compileEditorESMTask + ) + ), + finalEditorResourcesTask + ) +); //#region monaco type checking diff --git a/build/lib/util.js b/build/lib/util.js index 90b1e622cb1..b6228ad4cf9 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -209,21 +209,28 @@ var task; async function _execute(task) { // Always invoke as if it were a callback task return new Promise((resolve, reject) => { - const taskResult = task((err) => { - if (err) { - return reject(err); - } - resolve(); - }); + if (task.length === 1) { + // this is a calback task + task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + return; + } + const taskResult = task(); if (typeof taskResult === 'undefined') { - // this was a callback task + // this is a sync task + resolve(); return; } if (_isPromise(taskResult)) { - // this was a promise returning task + // this is a promise returning task taskResult.then(resolve, reject); return; } + // this is a stream returning task taskResult.on('end', _ => resolve()); taskResult.on('error', err => reject(err)); }); diff --git a/build/lib/util.ts b/build/lib/util.ts index 4ff07196612..42d3aba2fee 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -254,7 +254,7 @@ export function primraf(dir: string): Promise { export type PromiseTask = () => Promise; export type StreamTask = () => NodeJS.ReadWriteStream; -export type CallbackTask = (cb: (err?: any) => void) => void; +export type CallbackTask = (cb?: (err?: any) => void) => void; export type Task = PromiseTask | StreamTask | CallbackTask; export namespace task { @@ -269,24 +269,32 @@ export namespace task { async function _execute(task: Task): Promise { // Always invoke as if it were a callback task return new Promise((resolve, reject) => { - const taskResult = task((err) => { - if (err) { - return reject(err); - } - resolve(); - }); + if (task.length === 1) { + // this is a calback task + task((err) => { + if (err) { + return reject(err); + } + resolve(); + }); + return; + } + + const taskResult = task(); if (typeof taskResult === 'undefined') { - // this was a callback task + // this is a sync task + resolve(); return; } if (_isPromise(taskResult)) { - // this was a promise returning task + // this is a promise returning task taskResult.then(resolve, reject); return; } + // this is a stream returning task taskResult.on('end', _ => resolve()); taskResult.on('error', err => reject(err)); }); From 209ad59b91df43b4d46ec3f1a0592a82572e795a Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 19:24:51 +0100 Subject: [PATCH 1017/1066] Remove unused import --- 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 64e526e262b..5e947eb2052 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; -import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, createStyleSheet } from 'vs/base/browser/dom'; +import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; From 6063e04fee84bbd61763d92b39d6cf86cec3e22a Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 5 Feb 2019 10:43:52 -0800 Subject: [PATCH 1018/1066] fix light menu fg theming fixes #67918 --- src/vs/platform/theme/common/colorRegistry.ts | 2 +- src/vs/platform/theme/common/styler.ts | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 8c94876cb23..3fb34ab3b6c 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -257,7 +257,7 @@ export const scrollbarSliderActiveBackground = registerColor('scrollbarSlider.ac export const progressBarBackground = registerColor('progressBar.background', { dark: Color.fromHex('#0E70C0'), light: Color.fromHex('#0E70C0'), hc: contrastBorder }, nls.localize('progressBarBackground', "Background color of the progress bar that can show for long running operations.")); export const menuBorder = registerColor('menu.border', { dark: null, light: null, hc: contrastBorder }, nls.localize('menuBorder', "Border color of menus.")); -export const menuForeground = registerColor('menu.foreground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, nls.localize('menuForeground', "Foreground color of menu items.")); +export const menuForeground = registerColor('menu.foreground', { dark: selectForeground, light: foreground, hc: selectForeground }, nls.localize('menuForeground', "Foreground color of menu items.")); export const menuBackground = registerColor('menu.background', { dark: selectBackground, light: selectBackground, hc: selectBackground }, nls.localize('menuBackground', "Background color of menu items.")); export const menuSelectionForeground = registerColor('menu.selectionForeground', { dark: listActiveSelectionForeground, light: listActiveSelectionForeground, hc: listActiveSelectionForeground }, nls.localize('menuSelectionForeground', "Foreground color of the selected menu item in menus.")); export const menuSelectionBackground = registerColor('menu.selectionBackground', { dark: listActiveSelectionBackground, light: listActiveSelectionBackground, hc: listActiveSelectionBackground }, nls.localize('menuSelectionBackground', "Background color of the selected menu item in menus.")); diff --git a/src/vs/platform/theme/common/styler.ts b/src/vs/platform/theme/common/styler.ts index d7725d4d1ca..eac33a668a9 100644 --- a/src/vs/platform/theme/common/styler.ts +++ b/src/vs/platform/theme/common/styler.ts @@ -327,10 +327,5 @@ export const defaultMenuStyles = { }; export function attachMenuStyler(widget: IThemable, themeService, style?: IMenuStyleOverrides): IDisposable { - const styles = { ...defaultMenuStyles, ...style }; - const fallback: IMenuStyleOverrides = { - foregroundColor: !!styles.foregroundColor && !!themeService && !!themeService.getTheme().getColor(styles.foregroundColor) ? styles.foregroundColor : foreground - }; - - return attachStyler(themeService, { ...styles, ...fallback }, widget); + return attachStyler(themeService, { ...defaultMenuStyles, ...style }, widget); } From a9a726aa0dede7b0dbbaef25aea7cc7b23b74ac5 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Feb 2019 11:37:08 -0800 Subject: [PATCH 1019/1066] vscode-xterm@3.12.0-beta2 Fixes #67933 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5f78d0e4027..d1ccfcda47d 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.12.0-beta1", + "vscode-xterm": "3.12.0-beta2", "winreg": "^1.2.4", "yauzl": "^2.9.1", "yazl": "^2.4.3" diff --git a/yarn.lock b/yarn.lock index bfb1e38ac88..37c70a4517c 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.12.0-beta1: - version "3.12.0-beta1" - resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.12.0-beta1.tgz#7dc28214077d1d8fc306fb6f392b4b553c6b0024" - integrity sha512-bv6AbbtzMfNdku97BomTY7PJNo1Bd0sHnNy9GZmMeVm8SoLxxvhmR2PRsbYY0AD1n7wbA7l55msJKye57+mD/w== +vscode-xterm@3.12.0-beta2: + version "3.12.0-beta2" + resolved "https://registry.yarnpkg.com/vscode-xterm/-/vscode-xterm-3.12.0-beta2.tgz#fbbf6d1926e0628d4e0b85b1bfdefa2b0bf40df6" + integrity sha512-NhLYyJXjwLXXzn+NYEOkAtTxbdbAGkjztabE2Vy8j4fXXOwHHHg6KurMCbL7bd8BOEMIPrTIw6zLhzfexs5Vhw== vso-node-api@6.1.2-preview: version "6.1.2-preview" From c064781f92ddac447a24cbd4df88f0a61c9a2006 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Feb 2019 12:05:46 -0800 Subject: [PATCH 1020/1066] Fix #66151 - refactor settings editor layout --- .../parts/preferences/browser/settingsTree.ts | 44 ++++--- .../preferences/browser/settingsWidgets.ts | 10 +- .../media/settingsEditor2.css | 123 ++++++++---------- .../electron-browser/settingsEditor2.ts | 2 +- 4 files changed, 84 insertions(+), 95 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 9f13b1ae66e..67bc7db7bbe 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -256,6 +256,8 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer { templateId = SETTINGS_TEXT_TEMPLATE_ID; - renderTemplate(container: HTMLElement): ISettingTextItemTemplate { - const common = this.renderCommonTemplate(null, container, 'text'); - const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message')); + renderTemplate(_container: HTMLElement): ISettingTextItemTemplate { + const common = this.renderCommonTemplate(null, _container, 'text'); + const validationErrorMessageElement = DOM.append(common.containerElement, $('.setting-item-validation-message')); const inputBox = new InputBox(common.controlElement, this._contextViewService); common.toDispose.push(inputBox); @@ -851,9 +855,9 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre export class SettingNumberRenderer extends AbstractSettingRenderer implements ITreeRenderer { templateId = SETTINGS_NUMBER_TEMPLATE_ID; - renderTemplate(container: HTMLElement): ISettingNumberItemTemplate { - const common = super.renderCommonTemplate(null, container, 'number'); - const validationErrorMessageElement = DOM.append(container, $('.setting-item-validation-message')); + renderTemplate(_container: HTMLElement): ISettingNumberItemTemplate { + const common = super.renderCommonTemplate(null, _container, 'number'); + const validationErrorMessageElement = DOM.append(common.containerElement, $('.setting-item-validation-message')); const inputBox = new InputBox(common.controlElement, this._contextViewService, { type: 'number' }); common.toDispose.push(inputBox); @@ -906,9 +910,11 @@ export class SettingNumberRenderer extends AbstractSettingRenderer implements IT export class SettingBoolRenderer extends AbstractSettingRenderer implements ITreeRenderer { templateId = SETTINGS_BOOL_TEMPLATE_ID; - renderTemplate(container: HTMLElement): ISettingBoolItemTemplate { - DOM.addClass(container, 'setting-item'); - DOM.addClass(container, 'setting-item-bool'); + renderTemplate(_container: HTMLElement): ISettingBoolItemTemplate { + DOM.addClass(_container, 'setting-item'); + DOM.addClass(_container, 'setting-item-bool'); + + const container = DOM.append(_container, $(AbstractSettingRenderer.CONTENTS_SELECTOR)); const titleElement = DOM.append(container, $('.setting-item-title')); const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); @@ -1073,7 +1079,7 @@ export class SettingTreeRenderers { } getSettingDOMElementForDOMElement(domElement: HTMLElement): HTMLElement { - const parent = DOM.findParentWithClass(domElement, 'setting-item'); + const parent = DOM.findParentWithClass(domElement, AbstractSettingRenderer.CONTENTS_CLASS); if (parent) { return parent; } @@ -1324,27 +1330,27 @@ export class SettingsTree extends ObjectTree { // Links appear inside other elements in markdown. CSS opacity acts like a mask. So we have to dynamically compute the description color to avoid // applying an opacity to the link color. const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { color: ${fgWithOpacity}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); } const errorColor = theme.getColor(errorForeground); if (errorColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { color: ${errorColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-deprecation-message { color: ${errorColor}; }`); } const invalidInputBackground = theme.getColor(inputValidationErrorBackground); if (invalidInputBackground) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { background-color: ${invalidInputBackground}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-validation-message { background-color: ${invalidInputBackground}; }`); } const invalidInputForeground = theme.getColor(inputValidationErrorForeground); if (invalidInputForeground) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { color: ${invalidInputForeground}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-validation-message { color: ${invalidInputForeground}; }`); } const invalidInputBorder = theme.getColor(inputValidationErrorBorder); if (invalidInputBorder) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-validation-message { border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-control .monaco-inputbox.idle { outline-width: 0; border-style:solid; border-width: 1px; border-color: ${invalidInputBorder}; }`); } @@ -1356,7 +1362,7 @@ export class SettingsTree extends ObjectTree { const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:focus { outline-color: ${focusBorderColor} }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:focus { outline-color: ${focusBorderColor} }`); } })); diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts index bde2e02e2b0..39d3f19aae5 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -63,16 +63,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const link = theme.getColor(textLinkForeground); if (link) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a { color: ${link}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a > code { color: ${link}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a { color: ${link}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a > code { color: ${link}; }`); collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a { color: ${link}; }`); collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a > code { color: ${link}; }`); } const activeLink = theme.getColor(textLinkActiveForeground); if (activeLink) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active { color: ${activeLink}; }`); - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover > code, .settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:active > code { color: ${activeLink}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:hover, .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:active { color: ${activeLink}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:hover > code, .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:active > code { color: ${activeLink}; }`); collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active { color: ${activeLink}; }`); collector.addRule(`.monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:hover > code, .monaco-select-box-dropdown-container > .select-box-details-pane > .select-box-description-markdown a:active > code { color: ${activeLink}; }`); } @@ -127,7 +127,7 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const modifiedItemIndicatorColor = theme.getColor(modifiedItemIndicator); if (modifiedItemIndicatorColor) { - collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item > .setting-item-modified-indicator { border-color: ${modifiedItemIndicatorColor}; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents > .setting-item-modified-indicator { border-color: ${modifiedItemIndicatorColor}; }`); } }); 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 0674380596b..3c84a5c7efe 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -122,31 +122,9 @@ text-decoration: underline; } -.settings-editor.narrow-width > .settings-body .settings-tree-container .monaco-list-rows { - margin-left: 0px; -} - -.settings-editor > .settings-body .settings-tree-container .monaco-list-rows { - max-width: 1000px; - margin: auto; -} - -.settings-editor > .settings-body .settings-tree-container .monaco-list-row { - line-height: 1.4em !important; /* TODO */ - padding-left: 208px; - padding-right: 24px; - /* box-sizing: border-box; */ -} - -.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-row { - position: relative; -} - -.settings-editor.no-toc-search > .settings-body .settings-tree-container .monaco-list-row, -.settings-editor.narrow-width > .settings-body .settings-tree-container .monaco-list-row { - /* 3 margin + 20 padding + 2 border */ - width: calc(100% - 25px); - padding-left: 25px; +.settings-editor.no-toc-search > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents, +.settings-editor.narrow-width > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents { + padding-left: 33px; } .settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-twistie { @@ -162,14 +140,15 @@ .settings-editor > .settings-body > .settings-tree-container .shadow.top { left: 50%; - max-width: 952px; /* 1000 - 24*2 padding */ + max-width: 952px; + /* 1000 - 24*2 padding */ margin-left: -476px; z-index: 1000; } .settings-editor > .settings-body .settings-tree-container .setting-toolbar-container { position: absolute; - left: -23px; + left: -32px; top: 11px; bottom: 0px; width: 26px; @@ -270,7 +249,6 @@ padding-left: 31px; } -.settings-editor > .settings-body .settings-tree-container .monaco-list-rows, .settings-editor > .settings-body .settings-toc-wrapper { height: 100%; max-width: 1000px; @@ -283,94 +261,105 @@ } .settings-editor > .settings-body > .settings-tree-container .monaco-list-row { + line-height: 1.4em !important; + + /* so validation messages don't get clipped */ overflow: visible; - /* so validation messages dont get clipped */ cursor: default; } -.settings-editor > .settings-body > .settings-tree-container .setting-item { - padding-top: 12px; - padding-bottom: 18px; +.settings-editor > .settings-body .settings-tree-container .monaco-list-row .monaco-tl-contents { + max-width: 1000px; + margin: auto; box-sizing: border-box; - white-space: normal; - height: 100%; + padding-left: 217px; + padding-right: 20px; + overflow: visible; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents { + position: relative; + padding-top: 12px; + padding-bottom: 18px; + white-space: normal; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - display: inline-block; /* size to contents for hover to show context button */ + display: inline-block; + /* size to contents for hover to show context button */ } -.settings-editor > .settings-body > .settings-tree-container .setting-item > .setting-item-modified-indicator { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-modified-indicator { display: none; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.is-configured > .setting-item-modified-indicator { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents.is-configured .setting-item-modified-indicator { display: block; content: ' '; position: absolute; width: 6px; border-left-width: 2px; border-left-style: solid; - left: 0px; + left: -9px; top: 15px; bottom: 16px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item-bool.is-configured > .setting-item-modified-indicator { +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents.is-configured .setting-item-modified-indicator { bottom: 23px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-overrides { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides { opacity: 0.5; font-style: italic; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-title .setting-item-overrides a.modified-scope { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides a.modified-scope { text-decoration: underline; cursor: pointer; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-label { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-label { margin-right: 7px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-cat-label-container { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-cat-label-container { float: left; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-label, -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-label, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-category { font-weight: 600; user-select: text; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-category { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-category { opacity: 0.9; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-deprecation-message { margin-top: 3px; user-select: text; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { margin-top: -1px; user-select: text; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-deprecation-message { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-deprecation-message { position: absolute; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-validation-message { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-validation-message { display: none; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.invalid-input .setting-item-validation-message { +.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-contents.invalid-input .setting-item-validation-message { display: block; position: absolute; padding: 5px; @@ -392,35 +381,35 @@ -webkit-appearance: none !important; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown * { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown * { margin: 0px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:focus { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:focus { outline: 1px solid -webkit-focus-ring-color; outline-offset: -1px; text-decoration: underline; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown a:hover { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown a:hover { text-decoration: underline; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-description-markdown code { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description-markdown code { line-height: 15px; /** For some reason, this is needed, otherwise will take up 20px height */ font-family: Menlo, Monaco, Consolas, "Droid Sans Mono", "Courier New", monospace, "Droid Sans Fallback"; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-enumDescription { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-enumDescription { display: none; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-enumDescription { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-enumDescription { display: block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-bool { +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-item-contents { padding-bottom: 26px; } @@ -449,7 +438,7 @@ background: url('check-inverse.svg') center center no-repeat; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-value { margin-top: 9px; display: flex; } @@ -471,15 +460,15 @@ width: 320px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value .edit-in-settings-button, -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value .edit-in-settings-button:hover, -.settings-editor > .settings-body > .settings-tree-container .setting-item .setting-item-value .edit-in-settings-button:active { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-value .edit-in-settings-button, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-value .edit-in-settings-button:hover, +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-value .edit-in-settings-button:active { text-align: left; text-decoration: underline; padding-left: 0px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item .monaco-select-box { +.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .monaco-select-box { width: initial; font: inherit; height: 26px; @@ -495,12 +484,6 @@ padding: 4px 10px; } -.settings-editor > .settings-body > .settings-tree-container .group-title, -.settings-editor > .settings-body > .settings-tree-container .setting-item { - padding-left: 9px; - padding-right: 9px; -} - .settings-editor > .settings-body > .settings-tree-container .group-title { cursor: default; } diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 3d677bad6fd..ea9a1420deb 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -664,7 +664,7 @@ export class SettingsEditor2 extends BaseEditor { } // Hack, see https://github.com/Microsoft/vscode/issues/64749 - const settingItems = this.settingsTree.getHTMLElement().querySelectorAll('.setting-item'); + const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); const firstEl = settingItems[1] || settingItems[0]; if (!firstEl) { return; From 84bebd0ff6ec0c4ba57eeb5ee757e5d5ef457e20 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 21:23:25 +0100 Subject: [PATCH 1021/1066] Final adoption (#67863) --- build/gulpfile.editor.js | 9 +- build/gulpfile.extensions.js | 12 ++- build/gulpfile.vscode.js | 162 ++++++++++++++++++--------------- build/gulpfile.vscode.linux.js | 61 ++++++++----- build/gulpfile.vscode.win32.js | 7 +- gulpfile.js | 22 +++-- 6 files changed, 160 insertions(+), 113 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index eb59eb533e1..2c68dc74185 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -370,7 +370,12 @@ function createTscCompileTask(watch) { }; } -gulp.task('monaco-typecheck-watch', createTscCompileTask(true)); -gulp.task('monaco-typecheck', createTscCompileTask(false)); +const monacoTypecheckWatchTask = createTscCompileTask(true); +monacoTypecheckWatchTask.displayName = 'monaco-typecheck-watch'; +exports.monacoTypecheckWatchTask = monacoTypecheckWatchTask; + +const monacoTypecheckTask = createTscCompileTask(false); +monacoTypecheckTask.displayName = 'monaco-typecheck'; +exports.monacoTypecheckTask = monacoTypecheckTask; //#endregion diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index af3278cde5e..3a780a22e8d 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -141,10 +141,16 @@ const tasks = compilations.map(function (tsconfigFile) { }; }); -gulp.task('compile-extensions', util.task.parallel(...tasks.map(t => t.compileTask))); -gulp.task('watch-extensions', util.task.parallel(...tasks.map(t => t.watchTask))); +const compileExtensionsTask = util.task.parallel(...tasks.map(t => t.compileTask)); +compileExtensionsTask.displayName = 'compile-extensions'; +gulp.task(compileExtensionsTask.displayName, compileExtensionsTask); +exports.compileExtensionsTask = compileExtensionsTask; + +const watchExtensionsTask = util.task.parallel(...tasks.map(t => t.watchTask)); +watchExtensionsTask.displayName = 'watch-extensions'; +gulp.task(watchExtensionsTask.displayName, watchExtensionsTask); +exports.watchExtensionsTask = watchExtensionsTask; const compileExtensionsBuildTask = util.task.parallel(...tasks.map(t => t.compileBuildTask)); compileExtensionsBuildTask.displayName = 'compile-extensions-build'; - exports.compileExtensionsBuildTask = compileExtensionsBuildTask; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index c5887cde3f7..1cbd189b79e 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -104,7 +104,6 @@ const optimizeVSCodeTask = util.task.series( }) ); optimizeVSCodeTask.displayName = 'optimize-vscode'; -gulp.task(optimizeVSCodeTask.displayName, optimizeVSCodeTask); const optimizeIndexJSTask = util.task.series( @@ -492,30 +491,40 @@ const apiHostname = process.env.TRANSIFEX_API_URL; const apiName = process.env.TRANSIFEX_API_NAME; const apiToken = process.env.TRANSIFEX_API_TOKEN; -gulp.task('vscode-translations-push', ['optimize-vscode'], function () { - const pathToMetadata = './out-vscode/nls.metadata.json'; - const pathToExtensions = './extensions/*'; - const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; +gulp.task('vscode-translations-push', + util.task.series( + optimizeVSCodeTask, + function () { + const pathToMetadata = './out-vscode/nls.metadata.json'; + const pathToExtensions = './extensions/*'; + const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; - return es.merge( - gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), - gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), - gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) - ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken) - ).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken)); -}); + return es.merge( + gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), + gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), + gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) + ).pipe(i18n.findObsoleteResources(apiHostname, apiName, apiToken) + ).pipe(i18n.pushXlfFiles(apiHostname, apiName, apiToken)); + } + ) +); -gulp.task('vscode-translations-export', ['optimize-vscode'], function () { - const pathToMetadata = './out-vscode/nls.metadata.json'; - const pathToExtensions = './extensions/*'; - const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; +gulp.task('vscode-translations-export', + util.task.series( + optimizeVSCodeTask, + function () { + const pathToMetadata = './out-vscode/nls.metadata.json'; + const pathToExtensions = './extensions/*'; + const pathToSetup = 'build/win32/**/{Default.isl,messages.en.isl}'; - return es.merge( - gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), - gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), - gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) - ).pipe(vfs.dest('../vscode-translations-export')); -}); + return es.merge( + gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), + gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), + gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) + ).pipe(vfs.dest('../vscode-translations-export')); + } + ) +); gulp.task('vscode-translations-pull', function () { return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => { @@ -565,57 +574,8 @@ gulp.task('upload-vscode-sourcemaps', () => { })); }); -const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); -gulp.task('upload-vscode-configuration', ['generate-vscode-configuration'], () => { - if (!shouldSetupSettingsSearch()) { - const branch = process.env.BUILD_SOURCEBRANCH; - console.log(`Only runs on master and release branches, not ${branch}`); - return; - } - - if (!fs.existsSync(allConfigDetailsPath)) { - throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`); - } - - const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); - if (!settingsSearchBuildId) { - throw new Error('Failed to compute build number'); - } - - return gulp.src(allConfigDetailsPath) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - key: process.env.AZURE_STORAGE_ACCESS_KEY, - container: 'configuration', - prefix: `${settingsSearchBuildId}/${commit}/` - })); -}); - -function shouldSetupSettingsSearch() { - const branch = process.env.BUILD_SOURCEBRANCH; - return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); -} - -function getSettingsSearchBuildId(packageJson) { - try { - const branch = process.env.BUILD_SOURCEBRANCH; - const branchId = branch.indexOf('/release/') >= 0 ? 0 : - /\/master$/.test(branch) ? 1 : - 2; // Some unexpected branch - - const out = cp.execSync(`git rev-list HEAD --count`); - const count = parseInt(out.toString()); - - // - // 1.25.1, 1,234,567 commits, master = 1250112345671 - return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; - } catch (e) { - throw new Error('Could not determine build number: ' + e.toString()); - } -} - // This task is only run for the MacOS build -gulp.task('generate-vscode-configuration', () => { +const generateVSCodeConfigurationTask = () => { return new Promise((resolve, reject) => { const buildDir = process.env['AGENT_BUILDDIRECTORY']; if (!buildDir) { @@ -650,4 +610,60 @@ gulp.task('generate-vscode-configuration', () => { reject(err); }); }); -}); +}; +generateVSCodeConfigurationTask.displayName = 'generate-vscode-configuration'; + +const allConfigDetailsPath = path.join(os.tmpdir(), 'configuration.json'); +gulp.task('upload-vscode-configuration', + util.task.series( + generateVSCodeConfigurationTask, + () => { + if (!shouldSetupSettingsSearch()) { + const branch = process.env.BUILD_SOURCEBRANCH; + console.log(`Only runs on master and release branches, not ${branch}`); + return; + } + + if (!fs.existsSync(allConfigDetailsPath)) { + throw new Error(`configuration file at ${allConfigDetailsPath} does not exist`); + } + + const settingsSearchBuildId = getSettingsSearchBuildId(packageJson); + if (!settingsSearchBuildId) { + throw new Error('Failed to compute build number'); + } + + return gulp.src(allConfigDetailsPath) + .pipe(azure.upload({ + account: process.env.AZURE_STORAGE_ACCOUNT, + key: process.env.AZURE_STORAGE_ACCESS_KEY, + container: 'configuration', + prefix: `${settingsSearchBuildId}/${commit}/` + })); + } + ) +); + +function shouldSetupSettingsSearch() { + const branch = process.env.BUILD_SOURCEBRANCH; + return branch && (/\/master$/.test(branch) || branch.indexOf('/release/') >= 0); +} + +function getSettingsSearchBuildId(packageJson) { + try { + const branch = process.env.BUILD_SOURCEBRANCH; + const branchId = branch.indexOf('/release/') >= 0 ? 0 : + /\/master$/.test(branch) ? 1 : + 2; // Some unexpected branch + + const out = cp.execSync(`git rev-list HEAD --count`); + const count = parseInt(out.toString()); + + // + // 1.25.1, 1,234,567 commits, master = 1250112345671 + return util.versionStringToNumber(packageJson.version) * 1e8 + count * 10 + branchId; + } catch (e) { + throw new Error('Could not determine build number: ' + e.toString()); + } +} + diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 6728a0ce640..b3599cbe39d 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -229,29 +229,42 @@ function buildSnapPackage(arch) { return shell.task(`cd ${snapBuildPath} && snapcraft build`); } -gulp.task('vscode-linux-ia32-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/i386'), prepareDebPackage('ia32'))); -gulp.task('vscode-linux-x64-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/amd64'), prepareDebPackage('x64'))); -gulp.task('vscode-linux-arm-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/armhf'), prepareDebPackage('arm'))); -gulp.task('vscode-linux-arm64-prepare-deb', util.task.series(util.rimraf('.build/linux/deb/arm64'), prepareDebPackage('arm64'))); -gulp.task('vscode-linux-ia32-build-deb', ['vscode-linux-ia32-prepare-deb'], buildDebPackage('ia32')); -gulp.task('vscode-linux-x64-build-deb', ['vscode-linux-x64-prepare-deb'], buildDebPackage('x64')); -gulp.task('vscode-linux-arm-build-deb', ['vscode-linux-arm-prepare-deb'], buildDebPackage('arm')); -gulp.task('vscode-linux-arm64-build-deb', ['vscode-linux-arm64-prepare-deb'], buildDebPackage('arm64')); +const BUILD_TARGETS = [ + { arch: 'ia32' }, + { arch: 'x64' }, + { arch: 'arm' }, + { arch: 'arm64' }, +]; -gulp.task('vscode-linux-ia32-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/i386'), prepareRpmPackage('ia32'))); -gulp.task('vscode-linux-x64-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/x86_64'), prepareRpmPackage('x64'))); -gulp.task('vscode-linux-arm-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/armhf'), prepareRpmPackage('arm'))); -gulp.task('vscode-linux-arm64-prepare-rpm', util.task.series(util.rimraf('.build/linux/rpm/arm64'), prepareRpmPackage('arm64'))); -gulp.task('vscode-linux-ia32-build-rpm', ['vscode-linux-ia32-prepare-rpm'], buildRpmPackage('ia32')); -gulp.task('vscode-linux-x64-build-rpm', ['vscode-linux-x64-prepare-rpm'], buildRpmPackage('x64')); -gulp.task('vscode-linux-arm-build-rpm', ['vscode-linux-arm-prepare-rpm'], buildRpmPackage('arm')); -gulp.task('vscode-linux-arm64-build-rpm', ['vscode-linux-arm64-prepare-rpm'], buildRpmPackage('arm64')); +BUILD_TARGETS.forEach((buildTarget) => { + const arch = buildTarget.arch; -gulp.task('vscode-linux-ia32-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('ia32'))); -gulp.task('vscode-linux-x64-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('x64'))); -gulp.task('vscode-linux-arm-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('arm'))); -gulp.task('vscode-linux-arm64-prepare-snap', util.task.series(util.rimraf('.build/linux/snap/x64'), prepareSnapPackage('arm64'))); -gulp.task('vscode-linux-ia32-build-snap', ['vscode-linux-ia32-prepare-snap'], buildSnapPackage('ia32')); -gulp.task('vscode-linux-x64-build-snap', ['vscode-linux-x64-prepare-snap'], buildSnapPackage('x64')); -gulp.task('vscode-linux-arm-build-snap', ['vscode-linux-arm-prepare-snap'], buildSnapPackage('arm')); -gulp.task('vscode-linux-arm64-build-snap', ['vscode-linux-arm64-prepare-snap'], buildSnapPackage('arm64')); + { + const debArch = getDebPackageArch(arch); + const prepareDebTask = util.task.series(util.rimraf(`.build/linux/deb/${debArch}`), prepareDebPackage(arch)); + prepareDebTask.displayName = `vscode-linux-${arch}-prepare-deb`; + // gulp.task(prepareDebTask.displayName, prepareDebTask); + const buildDebTask = util.task.series(prepareDebTask, buildDebPackage(arch)); + buildDebTask.displayName = `vscode-linux-${arch}-build-deb`; + gulp.task(buildDebTask.displayName, buildDebTask); + } + + { + const rpmArch = getRpmPackageArch(arch); + const prepareRpmTask = util.task.series(util.rimraf(`.build/linux/rpm/${rpmArch}`), prepareRpmPackage(arch)); + prepareRpmTask.displayName = `vscode-linux-${arch}-prepare-rpm`; + // gulp.task(prepareRpmTask.displayName, prepareRpmTask); + const buildRpmTask = util.task.series(prepareRpmTask, buildRpmPackage(arch)); + buildRpmTask.displayName = `vscode-linux-${arch}-build-rpm`; + gulp.task(buildRpmTask.displayName, buildRpmTask); + } + + { + const prepareSnapTask = util.task.series(util.rimraf(`.build/linux/snap/${arch}`), prepareSnapPackage(arch)); + prepareSnapTask.displayName = `vscode-linux-${arch}-prepare-snap`; + gulp.task(prepareSnapTask.displayName, prepareSnapTask); + const buildSnapTask = util.task.series(prepareSnapTask, buildSnapPackage(arch)); + buildSnapTask.displayName = `vscode-linux-${arch}-build-snap`; + gulp.task(buildSnapTask.displayName, buildSnapTask); + } +}); diff --git a/build/gulpfile.vscode.win32.js b/build/gulpfile.vscode.win32.js index 3b3a21c1b69..b49fcbabcfc 100644 --- a/build/gulpfile.vscode.win32.js +++ b/build/gulpfile.vscode.win32.js @@ -134,9 +134,6 @@ function copyInnoUpdater(arch) { }; } -gulp.task('vscode-win32-ia32-copy-inno-updater', copyInnoUpdater('ia32')); -gulp.task('vscode-win32-x64-copy-inno-updater', copyInnoUpdater('x64')); - function patchInnoUpdater(arch) { return cb => { const icon = path.join(repoPath, 'resources', 'win32', 'code.ico'); @@ -144,5 +141,5 @@ function patchInnoUpdater(arch) { }; } -gulp.task('vscode-win32-ia32-inno-updater', ['vscode-win32-ia32-copy-inno-updater'], patchInnoUpdater('ia32')); -gulp.task('vscode-win32-x64-inno-updater', ['vscode-win32-x64-copy-inno-updater'], patchInnoUpdater('x64')); \ No newline at end of file +gulp.task('vscode-win32-ia32-inno-updater', util.task.series(copyInnoUpdater('ia32'), patchInnoUpdater('ia32'))); +gulp.task('vscode-win32-x64-inno-updater', util.task.series(copyInnoUpdater('x64'), patchInnoUpdater('x64'))); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 0f3fc9cc72c..b41fa640399 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -12,17 +12,27 @@ const gulp = require('gulp'); const util = require('./build/lib/util'); const path = require('path'); const compilation = require('./build/lib/compilation'); +const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./build/gulpfile.editor'); +const { compileExtensionsTask, watchExtensionsTask } = require('./build/gulpfile.extensions'); // Fast compile for development time -gulp.task('compile-client', util.task.series(util.rimraf('out'), compilation.compileTask('src', 'out', false))); -gulp.task('watch-client', util.task.series(util.rimraf('out'), compilation.watchTask('out', false))); +const compileClientTask = util.task.series(util.rimraf('out'), compilation.compileTask('src', 'out', false)); +compileClientTask.displayName = 'compile-client'; +gulp.task(compileClientTask.displayName, compileClientTask); -// Default -gulp.task('default', ['compile']); +const watchClientTask = util.task.series(util.rimraf('out'), compilation.watchTask('out', false)); +watchClientTask.displayName = 'watch-client'; +gulp.task(watchClientTask.displayName, watchClientTask); // All -gulp.task('compile', ['monaco-typecheck', 'compile-client', 'compile-extensions']); -gulp.task('watch', [/* 'monaco-typecheck-watch', */ 'watch-client', 'watch-extensions']); +const compileTask = util.task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask); +compileTask.displayName = 'compile'; +gulp.task(compileTask.displayName, compileTask); + +gulp.task('watch', util.task.parallel(/* monacoTypecheckWatchTask, */ watchClientTask, watchExtensionsTask)); + +// Default +gulp.task('default', compileTask); process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); From b4445a9f736a3a4d235391d9ff99d304acecd25e Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 22:21:05 +0100 Subject: [PATCH 1022/1066] Move away from deprecated gulp-util --- build/lib/builtInExtensions.js | 19 ++++++++++--------- build/lib/compilation.js | 5 +++-- build/lib/compilation.ts | 6 ++++-- build/lib/extensions.js | 7 ++++--- build/lib/extensions.ts | 7 ++++--- build/lib/i18n.js | 5 +++-- build/lib/i18n.ts | 6 +++--- build/lib/optimize.js | 5 +++-- build/lib/optimize.ts | 5 +++-- build/lib/reporter.js | 9 +++++---- build/lib/reporter.ts | 9 +++++---- build/lib/stats.js | 19 ++++++++++--------- build/lib/stats.ts | 19 ++++++++++--------- build/monaco/api.js | 5 +++-- build/monaco/api.ts | 5 +++-- build/package.json | 3 ++- build/yarn.lock | 24 ++++++++++++------------ package.json | 3 ++- yarn.lock | 20 ++++++++++++++++++++ 19 files changed, 109 insertions(+), 72 deletions(-) diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index c5797c78b61..a687bbccb4c 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -14,7 +14,8 @@ const es = require('event-stream'); const rename = require('gulp-rename'); const vfs = require('vinyl-fs'); const ext = require('./extensions'); -const util = require('gulp-util'); +const fancyLog = require('fancy-log'); +const ansiColors = require('ansi-colors'); const root = path.dirname(path.dirname(__dirname)); const builtInExtensions = require('../builtInExtensions.json'); @@ -43,7 +44,7 @@ function isUpToDate(extension) { function syncMarketplaceExtension(extension) { if (isUpToDate(extension)) { - util.log(util.colors.blue('[marketplace]'), `${extension.name}@${extension.version}`, util.colors.green('✔︎')); + fancyLog(ansiColors.blue('[marketplace]'), `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); return es.readArray([]); } @@ -52,13 +53,13 @@ function syncMarketplaceExtension(extension) { return ext.fromMarketplace(extension.name, extension.version, extension.metadata) .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)) .pipe(vfs.dest('.build/builtInExtensions')) - .on('end', () => util.log(util.colors.blue('[marketplace]'), extension.name, util.colors.green('✔︎'))); + .on('end', () => fancyLog(ansiColors.blue('[marketplace]'), extension.name, ansiColors.green('✔︎'))); } function syncExtension(extension, controlState) { switch (controlState) { case 'disabled': - util.log(util.colors.blue('[disabled]'), util.colors.gray(extension.name)); + fancyLog(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); return es.readArray([]); case 'marketplace': @@ -66,15 +67,15 @@ function syncExtension(extension, controlState) { default: if (!fs.existsSync(controlState)) { - util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); + fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); return es.readArray([]); } else if (!fs.existsSync(path.join(controlState, 'package.json'))) { - util.log(util.colors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); + fancyLog(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); return es.readArray([]); } - util.log(util.colors.blue('[local]'), `${extension.name}: ${util.colors.cyan(controlState)}`, util.colors.green('✔︎')); + fancyLog(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎')); return es.readArray([]); } } @@ -93,8 +94,8 @@ function writeControlFile(control) { } function main() { - util.log('Syncronizing built-in extensions...'); - util.log(`You can manage built-in extensions with the ${util.colors.cyan('--builtin')} flag`); + fancyLog('Syncronizing built-in extensions...'); + fancyLog(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index f0468121d7a..592a5d087ce 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -16,7 +16,8 @@ const monacodts = require("../monaco/api"); const nls = require("./nls"); const reporter_1 = require("./reporter"); const util = require("./util"); -const util2 = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const watch = require('./watch'); const reporter = reporter_1.createReporter(); function getTypeScriptCompilerOptions(src) { @@ -179,7 +180,7 @@ class MonacoGenerator { return r; } _log(message, ...rest) { - util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); + fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); } execute() { const startTime = Date.now(); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index fede9e74492..b431a134f6c 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -17,7 +17,9 @@ import * as monacodts from '../monaco/api'; import * as nls from './nls'; import { createReporter } from './reporter'; import * as util from './util'; -import * as util2 from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; + const watch = require('./watch'); const reporter = createReporter(); @@ -218,7 +220,7 @@ class MonacoGenerator { } private _log(message: any, ...rest: any[]): void { - util2.log(util2.colors.cyan('[monaco.d.ts]'), message, ...rest); + fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); } public execute(): void { diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 994d1336e6b..3554fabc230 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -17,7 +17,8 @@ const remote = require("gulp-remote-src"); const vzip = require('gulp-vinyl-zip'); const filter = require("gulp-filter"); const rename = require("gulp-rename"); -const util = require('gulp-util'); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const buffer = require('gulp-buffer'); const json = require("gulp-json-editor"); const webpack = require('webpack'); @@ -79,7 +80,7 @@ function fromLocalWebpack(extensionPath, sourceMappingURLBase) { .pipe(packageJsonFilter.restore); const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err, stats) => { - util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); + fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { result.emit('error', err); } @@ -157,7 +158,7 @@ const baseHeaders = { function fromMarketplace(extensionName, version, metadata) { const [publisher, name] = extensionName.split('.'); const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - util.log('Downloading extension:', util.colors.yellow(`${extensionName}@${version}`), '...'); + fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); const options = { base: url, requestOptions: { diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 844b711a829..7b89942753f 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -17,7 +17,8 @@ import remote = require('gulp-remote-src'); const vzip = require('gulp-vinyl-zip'); import filter = require('gulp-filter'); import rename = require('gulp-rename'); -const util = require('gulp-util'); +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; const buffer = require('gulp-buffer'); import json = require('gulp-json-editor'); const webpack = require('webpack'); @@ -93,7 +94,7 @@ function fromLocalWebpack(extensionPath: string, sourceMappingURLBase: string | const webpackStreams = webpackConfigLocations.map(webpackConfigPath => () => { const webpackDone = (err: any, stats: any) => { - util.log(`Bundled extension: ${util.colors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); + fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); if (err) { result.emit('error', err); } @@ -187,7 +188,7 @@ export function fromMarketplace(extensionName: string, version: string, metadata const [publisher, name] = extensionName.split('.'); const url = `https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - util.log('Downloading extension:', util.colors.yellow(`${extensionName}@${version}`), '...'); + fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); const options = { base: url, diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 1fc6b060e9c..24ed4456f50 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -13,11 +13,12 @@ const xml2js = require("xml2js"); const glob = require("glob"); const https = require("https"); const gulp = require("gulp"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const iconv = require("iconv-lite"); const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message, ...rest) { - util.log(util.colors.green('[i18n]'), message, ...rest); + fancyLog(ansiColors.green('[i18n]'), message, ...rest); } exports.defaultLanguages = [ { id: 'zh-tw', folderName: 'cht', transifexId: 'zh-hant' }, diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 81ba0c0939e..5cde7d8f87a 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -13,14 +13,14 @@ import * as xml2js from 'xml2js'; import * as glob from 'glob'; import * as https from 'https'; import * as gulp from 'gulp'; - -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as iconv from 'iconv-lite'; const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message: any, ...rest: any[]): void { - util.log(util.colors.green('[i18n]'), message, ...rest); + fancyLog(ansiColors.green('[i18n]'), message, ...rest); } export interface Language { diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 07cdebe30ee..3756d01f538 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -13,7 +13,8 @@ const flatmap = require("gulp-flatmap"); const sourcemaps = require("gulp-sourcemaps"); const uglify = require("gulp-uglify"); const composer = require("gulp-uglify/composer"); -const gulpUtil = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const path = require("path"); const pump = require("pump"); const uglifyes = require("uglify-es"); @@ -24,7 +25,7 @@ const stats_1 = require("./stats"); const util = require("./util"); const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix, message) { - gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); + fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } function loaderConfig(emptyPaths) { const result = { diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 85d15b23543..12b633b3ac9 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -14,7 +14,8 @@ import * as flatmap from 'gulp-flatmap'; import * as sourcemaps from 'gulp-sourcemaps'; import * as uglify from 'gulp-uglify'; import * as composer from 'gulp-uglify/composer'; -import * as gulpUtil from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as path from 'path'; import * as pump from 'pump'; import * as sm from 'source-map'; @@ -28,7 +29,7 @@ import * as util from './util'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(prefix: string, message: string): void { - gulpUtil.log(gulpUtil.colors.cyan('[' + prefix + ']'), message); + fancyLog(ansiColors.cyan('[' + prefix + ']'), message); } export function loaderConfig(emptyPaths?: string[]) { diff --git a/build/lib/reporter.js b/build/lib/reporter.js index 598aabec244..e0461dc6d9d 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -6,7 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); const _ = require("underscore"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const fs = require("fs"); const path = require("path"); const allErrors = []; @@ -17,7 +18,7 @@ function onStart() { return; } startTime = new Date().getTime(); - util.log(`Starting ${util.colors.green('compilation')}...`); + fancyLog(`Starting ${ansiColors.green('compilation')}...`); } function onEnd() { if (--count > 0) { @@ -38,7 +39,7 @@ function log() { errors.map(err => { if (!seen.has(err)) { seen.add(err); - util.log(`${util.colors.red('Error')}: ${err}`); + fancyLog(`${ansiColors.red('Error')}: ${err}`); } }); const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/; @@ -53,7 +54,7 @@ function log() { catch (err) { //noop } - util.log(`Finished ${util.colors.green('compilation')} with ${errors.length} errors after ${util.colors.magenta((new Date().getTime() - startTime) + ' ms')}`); + fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime) + ' ms')}`); } function createReporter() { const errors = []; diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index 68e037e9224..ec908817518 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -7,7 +7,8 @@ import * as es from 'event-stream'; import * as _ from 'underscore'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as fs from 'fs'; import * as path from 'path'; @@ -21,7 +22,7 @@ function onStart(): void { } startTime = new Date().getTime(); - util.log(`Starting ${util.colors.green('compilation')}...`); + fancyLog(`Starting ${ansiColors.green('compilation')}...`); } function onEnd(): void { @@ -47,7 +48,7 @@ function log(): void { errors.map(err => { if (!seen.has(err)) { seen.add(err); - util.log(`${util.colors.red('Error')}: ${err}`); + fancyLog(`${ansiColors.red('Error')}: ${err}`); } }); @@ -65,7 +66,7 @@ function log(): void { //noop } - util.log(`Finished ${util.colors.green('compilation')} with ${errors.length} errors after ${util.colors.magenta((new Date().getTime() - startTime!) + ' ms')}`); + fancyLog(`Finished ${ansiColors.green('compilation')} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - startTime!) + ' ms')}`); } export interface IReporter { diff --git a/build/lib/stats.js b/build/lib/stats.js index c08cd57b3bc..99ad665f223 100644 --- a/build/lib/stats.js +++ b/build/lib/stats.js @@ -5,7 +5,8 @@ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); const es = require("event-stream"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const appInsights = require("applicationinsights"); class Entry { constructor(name, totalCount, totalSize) { @@ -24,13 +25,13 @@ class Entry { } else { if (this.totalCount === 1) { - return `Stats for '${util.colors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; } else { const count = this.totalCount < 100 - ? util.colors.green(this.totalCount.toString()) - : util.colors.red(this.totalCount.toString()); - return `Stats for '${util.colors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; + ? ansiColors.green(this.totalCount.toString()) + : ansiColors.red(this.totalCount.toString()); + return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; } } } @@ -57,13 +58,13 @@ function createStatsStream(group, log) { }, function () { if (log) { if (entry.totalCount === 1) { - util.log(`Stats for '${util.colors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); } else { const count = entry.totalCount < 100 - ? util.colors.green(entry.totalCount.toString()) - : util.colors.red(entry.totalCount.toString()); - util.log(`Stats for '${util.colors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); + ? ansiColors.green(entry.totalCount.toString()) + : ansiColors.red(entry.totalCount.toString()); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); } } this.emit('end'); diff --git a/build/lib/stats.ts b/build/lib/stats.ts index 7f5548dc1c4..a94b1c9ae1a 100644 --- a/build/lib/stats.ts +++ b/build/lib/stats.ts @@ -6,7 +6,8 @@ 'use strict'; import * as es from 'event-stream'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; import * as File from 'vinyl'; import * as appInsights from 'applicationinsights'; @@ -22,14 +23,14 @@ class Entry { } } else { if (this.totalCount === 1) { - return `Stats for '${util.colors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; } else { const count = this.totalCount < 100 - ? util.colors.green(this.totalCount.toString()) - : util.colors.red(this.totalCount.toString()); + ? ansiColors.green(this.totalCount.toString()) + : ansiColors.red(this.totalCount.toString()); - return `Stats for '${util.colors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; } } } @@ -58,14 +59,14 @@ export function createStatsStream(group: string, log?: boolean): es.ThroughStrea }, function () { if (log) { if (entry.totalCount === 1) { - util.log(`Stats for '${util.colors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); } else { const count = entry.totalCount < 100 - ? util.colors.green(entry.totalCount.toString()) - : util.colors.red(entry.totalCount.toString()); + ? ansiColors.green(entry.totalCount.toString()) + : ansiColors.red(entry.totalCount.toString()); - util.log(`Stats for '${util.colors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); + fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); } } diff --git a/build/monaco/api.js b/build/monaco/api.js index 5c03df4088d..297510d14c1 100644 --- a/build/monaco/api.js +++ b/build/monaco/api.js @@ -7,14 +7,15 @@ Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const ts = require("typescript"); const path = require("path"); -const util = require("gulp-util"); +const fancyLog = require("fancy-log"); +const ansiColors = require("ansi-colors"); const dtsv = '2'; const tsfmt = require('../../tsfmt.json'); const SRC = path.join(__dirname, '../../src'); exports.RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message, ...rest) { - util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); + fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); } function isDeclaration(a) { return (a.kind === ts.SyntaxKind.InterfaceDeclaration diff --git a/build/monaco/api.ts b/build/monaco/api.ts index 1bdc612d6a2..7672188160b 100644 --- a/build/monaco/api.ts +++ b/build/monaco/api.ts @@ -6,7 +6,8 @@ import * as fs from 'fs'; import * as ts from 'typescript'; import * as path from 'path'; -import * as util from 'gulp-util'; +import * as fancyLog from 'fancy-log'; +import * as ansiColors from 'ansi-colors'; const dtsv = '2'; @@ -17,7 +18,7 @@ export const RECIPE_PATH = path.join(__dirname, './monaco.d.ts.recipe'); const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message: any, ...rest: any[]): void { - util.log(util.colors.yellow(`[monaco.d.ts]`), message, ...rest); + fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); } type SourceFileGetter = (moduleId: string) => ts.SourceFile | null; diff --git a/build/package.json b/build/package.json index 4ad039a7e21..a05a6b9a622 100644 --- a/build/package.json +++ b/build/package.json @@ -2,9 +2,11 @@ "name": "code-oss-dev-build", "version": "1.0.0", "devDependencies": { + "@types/ansi-colors": "^3.2.0", "@types/azure": "0.9.19", "@types/debounce": "^1.0.0", "@types/documentdb": "1.10.2", + "@types/fancy-log": "^1.3.0", "@types/glob": "^7.1.1", "@types/gulp": "^4.0.5", "@types/gulp-concat": "^0.0.32", @@ -13,7 +15,6 @@ "@types/gulp-rename": "^0.0.33", "@types/gulp-sourcemaps": "^0.0.32", "@types/gulp-uglify": "^3.0.5", - "@types/gulp-util": "^3.0.34", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.0", diff --git a/build/yarn.lock b/build/yarn.lock index c9384fe6d54..d5816c98f02 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -10,6 +10,11 @@ normalize-path "^2.0.1" through2 "^2.0.3" +"@types/ansi-colors@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/ansi-colors/-/ansi-colors-3.2.0.tgz#3e4fe85d9131ce1c6994f3040bd0b25306c16a6e" + integrity sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg== + "@types/azure@0.9.19": version "0.9.19" resolved "https://registry.yarnpkg.com/@types/azure/-/azure-0.9.19.tgz#1a6a9bd856b437ddecf3f9fc8407a683c869ba02" @@ -47,6 +52,11 @@ resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== +"@types/fancy-log@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/fancy-log/-/fancy-log-1.3.0.tgz#a61ab476e5e628cd07a846330df53b85e05c8ce0" + integrity sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw== + "@types/form-data@*": version "2.2.1" resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" @@ -117,16 +127,6 @@ "@types/node" "*" "@types/uglify-js" "^2" -"@types/gulp-util@^3.0.34": - version "3.0.34" - resolved "https://registry.yarnpkg.com/@types/gulp-util/-/gulp-util-3.0.34.tgz#d1291ebf706d93f46eb8df11344bbfd96247697e" - integrity sha512-E06WN1OfqL5UsMwJ1T7ClgnaXgaPipb7Ee8euMc3KRHLNqxdvWrDir9KA6uevgzBgT7XbjgmzZA2pkzDqBBX7A== - dependencies: - "@types/node" "*" - "@types/through2" "*" - "@types/vinyl" "*" - chalk "^2.2.0" - "@types/gulp@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-4.0.5.tgz#f5f498d5bf9538364792de22490a12c0e6bc5eb4" @@ -196,7 +196,7 @@ "@types/glob" "*" "@types/node" "*" -"@types/through2@*", "@types/through2@^2.0.34": +"@types/through2@^2.0.34": version "2.0.34" resolved "https://registry.yarnpkg.com/@types/through2/-/through2-2.0.34.tgz#9c2a259a238dace2a05a2f8e94b786961bc27ac4" integrity sha512-nhRG8+RuG/L+0fAZBQYaRflXKjTrHOKH8MFTChnf+dNVMxA3wHYYrfj0tztK0W51ABXjGfRCDc0vRkecCOrsow== @@ -512,7 +512,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.2.0, chalk@^2.3.0: +chalk@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== diff --git a/package.json b/package.json index d1ccfcda47d..869a2188709 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@types/sinon": "^1.16.36", "@types/webpack": "^4.4.10", "@types/winreg": "^1.2.30", + "ansi-colors": "^3.2.3", "asar": "^0.14.0", "chromium-pickle-js": "^0.2.0", "clean-css": "3.4.6", @@ -80,6 +81,7 @@ "eslint": "^3.4.0", "event-stream": "3.3.4", "express": "^4.13.1", + "fancy-log": "^1.3.3", "glob": "^5.0.13", "gulp": "^3.8.9", "gulp-atom-electron": "^1.19.2", @@ -100,7 +102,6 @@ "gulp-tsb": "2.0.5", "gulp-tslint": "^8.1.2", "gulp-uglify": "^3.0.0", - "gulp-util": "^3.0.6", "gulp-vinyl-zip": "^2.1.2", "husky": "^0.13.1", "innosetup-compiler": "^5.5.60", diff --git a/yarn.lock b/yarn.lock index 37c70a4517c..d485597df7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -370,6 +370,11 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" +ansi-colors@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" @@ -2851,6 +2856,16 @@ fancy-log@^1.3.2: color-support "^1.1.3" time-stamp "^1.0.0" +fancy-log@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" + integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== + dependencies: + ansi-gray "^0.1.1" + color-support "^1.1.3" + parse-node-version "^1.0.0" + time-stamp "^1.0.0" + fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" @@ -6495,6 +6510,11 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-node-version@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" From 929177ff6ab5482ddac6515332f81686ef3f4579 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Feb 2019 22:29:35 +0100 Subject: [PATCH 1023/1066] Remove unused gulp task --- build/gulpfile.test.js | 15 --------------- package.json | 1 - yarn.lock | 28 ++-------------------------- 3 files changed, 2 insertions(+), 42 deletions(-) delete mode 100644 build/gulpfile.test.js diff --git a/build/gulpfile.test.js b/build/gulpfile.test.js deleted file mode 100644 index ff89922100a..00000000000 --- a/build/gulpfile.test.js +++ /dev/null @@ -1,15 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -const gulp = require('gulp'); -const mocha = require('gulp-mocha'); - -gulp.task('test', function () { - return gulp.src('test/all.js') - .pipe(mocha({ ui: 'tdd', delay: true })) - .once('end', function () { process.exit(); }); -}); diff --git a/package.json b/package.json index 869a2188709..21e96b6c067 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,6 @@ "gulp-filter": "^3.0.0", "gulp-flatmap": "^1.0.0", "gulp-json-editor": "^2.2.1", - "gulp-mocha": "^2.1.3", "gulp-plumber": "^1.2.0", "gulp-remote-src": "^0.4.4", "gulp-rename": "^1.2.0", diff --git a/yarn.lock b/yarn.lock index d485597df7b..0385a824bf6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3689,18 +3689,6 @@ gulp-json-editor@^2.2.1: js-beautify "~1.5.4" through2 "~0.5.0" -gulp-mocha@^2.1.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/gulp-mocha/-/gulp-mocha-2.2.0.tgz#1ce5eba4b94b40c7436afec3c4982c8eea894192" - integrity sha1-HOXrpLlLQMdDav7DxJgsjuqJQZI= - dependencies: - gulp-util "^3.0.0" - mocha "^2.0.1" - plur "^2.1.0" - resolve-from "^1.0.0" - temp "^0.8.3" - through "^2.3.4" - gulp-plumber@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gulp-plumber/-/gulp-plumber-1.2.0.tgz#18ea03912c9ee483f8a5499973b5954cd90f6ad8" @@ -4338,11 +4326,6 @@ ipaddr.js@1.5.2: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" integrity sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A= -irregular-plurals@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" - integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= - is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -5805,7 +5788,7 @@ mocha-junit-reporter@^1.17.0: strip-ansi "^4.0.0" xml "^1.0.0" -mocha@^2.0.1, mocha@^2.2.5: +mocha@^2.2.5: version "2.5.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" integrity sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg= @@ -6711,13 +6694,6 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" -plur@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" - integrity sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo= - dependencies: - irregular-plurals "^1.0.0" - pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" @@ -8580,7 +8556,7 @@ through2@~0.4.0: readable-stream "~1.0.17" xtend "~2.1.1" -through@2, through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.8: +through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= From cf381901e4ba011c5954a7caaad28d97f2055cec Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Fri, 25 Jan 2019 09:48:11 -0800 Subject: [PATCH 1024/1066] Refactor terminal launch code --- .../parts/terminal/common/terminal.ts | 1 + .../electron-browser/terminalConfigHelper.ts | 53 +++++++++++++------ .../terminalProcessManager.ts | 9 +++- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/parts/terminal/common/terminal.ts b/src/vs/workbench/parts/terminal/common/terminal.ts index 8f61e7761ce..d36b3cbeae3 100644 --- a/src/vs/workbench/parts/terminal/common/terminal.ts +++ b/src/vs/workbench/parts/terminal/common/terminal.ts @@ -114,6 +114,7 @@ export interface ITerminalConfigHelper { mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride?: platform.Platform): void; /** Sets whether a workspace shell configuration is allowed or not */ setWorkspaceShellAllowed(isAllowed: boolean): void; + checkWorkspaceShellPermissions(platformOverride?: platform.Platform): boolean; } export interface ITerminalFont { diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 3321227befc..9aee7905251 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -163,20 +163,26 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, isAllowed, StorageScope.WORKSPACE); } - public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void { + public isWorkspaceShellAllowed(defaultValue: boolean | undefined = undefined): boolean | undefined { + return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, defaultValue); + } + + public checkWorkspaceShellPermissions(platformOverride: platform.Platform = platform.platform): boolean { // Check whether there is a workspace setting const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; const shellConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shell.${platformKey}`); const shellArgsConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shellArgs.${platformKey}`); + const envConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.env.${platformKey}`); // Check if workspace setting exists and whether it's whitelisted let isWorkspaceShellAllowed: boolean | undefined = false; - if (shellConfigValue.workspace !== undefined || shellArgsConfigValue.workspace !== undefined) { - isWorkspaceShellAllowed = this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, undefined); + if (shellConfigValue.workspace !== undefined || shellArgsConfigValue.workspace !== undefined || envConfigValue.workspace !== undefined) { + isWorkspaceShellAllowed = this.isWorkspaceShellAllowed(undefined); } // Always allow [] args as it would lead to an odd error message and should not be dangerous - if (shellConfigValue.workspace === undefined && shellArgsConfigValue.workspace && shellArgsConfigValue.workspace.length === 0) { + if (shellConfigValue.workspace === undefined && envConfigValue.workspace === undefined && + shellArgsConfigValue.workspace && shellArgsConfigValue.workspace.length === 0) { isWorkspaceShellAllowed = true; } @@ -185,34 +191,47 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { if (isWorkspaceShellAllowed === undefined) { let shellString: string | undefined; if (shellConfigValue.workspace) { - shellString = `"${shellConfigValue.workspace}"`; + shellString = `shell: "${shellConfigValue.workspace}"`; } let argsString: string | undefined; if (shellArgsConfigValue.workspace) { - argsString = `[${shellArgsConfigValue.workspace.map(v => '"' + v + '"').join(', ')}]`; + argsString = `shellArgs: [${shellArgsConfigValue.workspace.map(v => '"' + v + '"').join(', ')}]`; + } + let envString: string | undefined; + if (envConfigValue.workspace) { + envString = `env: {${Object.keys(envConfigValue.workspace).map(k => `${k}:${envConfigValue.workspace[k]}`).join(', ')}}`; } // Should not be localized as it's json-like syntax referencing settings keys - let changeString: string; + const workspaceConfigStrings: string[] = []; if (shellString) { - if (argsString) { - changeString = `shell: ${shellString}, shellArgs: ${argsString}`; - } else { - changeString = `shell: ${shellString}`; - } - } else { // if (shellArgsConfigValue.workspace !== undefined) - changeString = `shellArgs: ${argsString}`; + workspaceConfigStrings.push(shellString); } - this._notificationService.prompt(Severity.Info, nls.localize('terminal.integrated.allowWorkspaceShell', "Do you allow {0} (defined as a workspace setting) to be launched in the terminal?", changeString), + if (argsString) { + workspaceConfigStrings.push(argsString); + } + if (envString) { + workspaceConfigStrings.push(envString); + } + const workspaceConfigString = workspaceConfigStrings.join(', '); + this._notificationService.prompt(Severity.Info, nls.localize('terminal.integrated.allowWorkspaceShell', "Do you allow this workspace to modify your terminal shell? {0}", workspaceConfigString), [{ label: nls.localize('allow', "Allow"), - run: () => this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, true, StorageScope.WORKSPACE) + run: () => this.setWorkspaceShellAllowed(true) }, { label: nls.localize('disallow', "Disallow"), - run: () => this._storageService.store(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, false, StorageScope.WORKSPACE) + run: () => this.setWorkspaceShellAllowed(false) }] ); } + return !!isWorkspaceShellAllowed; + } + + public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void { + const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride); + const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; + const shellConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shell.${platformKey}`); + const shellArgsConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.shellArgs.${platformKey}`); shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || shellConfigValue.default; shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default; diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts index a49259ddcd4..466e6e7326d 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalProcessManager.ts @@ -20,6 +20,7 @@ import { IWindowService } from 'vs/platform/windows/common/windows'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { sanitizeProcessEnvironment } from 'vs/base/node/processes'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -58,7 +59,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { @ILogService private readonly _logService: ILogService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, - @IWindowService private readonly _windowService: IWindowService + @IWindowService private readonly _windowService: IWindowService, + @IWorkspaceConfigurationService private readonly _workspaceConfigurationService: IWorkspaceConfigurationService, ) { this.ptyProcessReady = new Promise(c => { this.onProcessReady(() => { @@ -125,7 +127,10 @@ export class TerminalProcessManager implements ITerminalProcessManager { // Resolve env vars from config and shell const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); - const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...this._configHelper.config.env[platformKey] }, lastActiveWorkspaceRoot); + const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); + const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`); + const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user); + const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot); const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); shellLaunchConfig.env = envFromShell; From 66614c07d359ac4cb8b5fd5569da501018df1af0 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 11:42:43 -0800 Subject: [PATCH 1025/1066] Strict null check quickopen --- src/tsconfig.strictNullChecks.json | 3 +++ src/vs/base/common/arrays.ts | 1 + src/vs/workbench/browser/quickopen.ts | 26 +++++++++++++------------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index edc118aff8d..4d89719082c 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -457,6 +457,7 @@ "./vs/workbench/browser/parts/views/panelViewlet.ts", "./vs/workbench/browser/parts/views/views.ts", "./vs/workbench/browser/parts/views/viewsViewlet.ts", + "./vs/workbench/browser/quickopen.ts", "./vs/workbench/browser/viewlet.ts", "./vs/workbench/browser/workbench.contribution.ts", "./vs/workbench/common/actions.ts", @@ -523,6 +524,7 @@ "./vs/workbench/parts/experiments/electron-browser/experimentalPrompt.ts", "./vs/workbench/parts/experiments/electron-browser/experiments.contribution.ts", "./vs/workbench/parts/experiments/node/experimentService.ts", + "./vs/workbench/parts/extensions/browser/extensionsQuickOpen.ts", "./vs/workbench/parts/extensions/browser/extensionsViewer.ts", "./vs/workbench/parts/extensions/common/extensionQuery.ts", "./vs/workbench/parts/extensions/common/extensions.ts", @@ -563,6 +565,7 @@ "./vs/workbench/parts/preferences/browser/settingsWidgets.ts", "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", + "./vs/workbench/parts/quickopen/browser/helpHandler.ts", "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", "./vs/workbench/parts/scm/common/scm.ts", "./vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts", diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index ab01e07bded..ce2f41cf633 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -393,6 +393,7 @@ export function firstIndex(array: ReadonlyArray, fn: (item: T) => boolean) export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T): T; export function first(array: ReadonlyArray, fn: (item: T) => boolean): T | null; +export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null): T | null; export function first(array: ReadonlyArray, fn: (item: T) => boolean, notFoundValue: T | null = null): T | null { const index = firstIndex(array, fn); return index < 0 ? notFoundValue : array[index]; diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index 97b8b9b2678..af06b1fbade 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -57,7 +57,7 @@ export class QuickOpenHandler { /** * Extra CSS class name to add to the quick open widget to do custom styling of entries. */ - getClass(): string { + getClass() { return null; } @@ -102,7 +102,7 @@ export class QuickOpenHandler { /** * Allows to return a label that will be placed to the side of the results from this handler or null if none. */ - getGroupLabel(): string { + getGroupLabel() { return null; } @@ -185,7 +185,7 @@ export interface IQuickOpenRegistry { /** * Get a specific quick open handler for a given prefix. */ - getQuickOpenHandler(prefix: string): QuickOpenHandlerDescriptor; + getQuickOpenHandler(prefix: string): QuickOpenHandlerDescriptor | null; /** * Returns the default quick open handler. @@ -213,8 +213,8 @@ class QuickOpenRegistry implements IQuickOpenRegistry { return this.handlers.slice(0); } - getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor { - return text ? arrays.first(this.handlers, h => strings.startsWith(text, h.prefix), null) : null; + getQuickOpenHandler(text: string): QuickOpenHandlerDescriptor | null { + return text ? arrays.first(this.handlers, h => strings.startsWith(text, h.prefix), null) : null; } getDefaultQuickOpenHandler(): QuickOpenHandlerDescriptor { @@ -229,12 +229,12 @@ export interface IEditorQuickOpenEntry { /** * The editor input used for this entry when opening. */ - getInput(): IResourceInput | IEditorInput; + getInput(): IResourceInput | IEditorInput | null; /** * The editor options used for this entry when opening. */ - getOptions(): IEditorOptions; + getOptions(): IEditorOptions | null; } /** @@ -250,11 +250,11 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick return this._editorService; } - getInput(): IResourceInput | IEditorInput { + getInput(): IResourceInput | IEditorInput | null { return null; } - getOptions(): IEditorOptions { + getOptions(): IEditorOptions | null { return null; } @@ -264,7 +264,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick if (mode === Mode.OPEN || mode === Mode.OPEN_IN_BACKGROUND) { const sideBySide = context.keymods.ctrlCmd; - let openOptions: IEditorOptions; + let openOptions: IEditorOptions | undefined; if (mode === Mode.OPEN_IN_BACKGROUND) { openOptions = { pinned: true, preserveFocus: true }; } else if (context.keymods.alt) { @@ -280,7 +280,7 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick opts = EditorOptions.create(openOptions); } - this.editorService.openEditor(input, opts, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); + this.editorService.openEditor(input, opts || undefined, sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } else { const resourceInput = input; @@ -301,11 +301,11 @@ export class EditorQuickOpenEntry extends QuickOpenEntry implements IEditorQuick */ export class EditorQuickOpenEntryGroup extends QuickOpenEntryGroup implements IEditorQuickOpenEntry { - getInput(): IEditorInput | IResourceInput { + getInput(): IEditorInput | IResourceInput | null { return null; } - getOptions(): IEditorOptions { + getOptions(): IEditorOptions | null { return null; } } From 815a3cb8600d8925add277939228913a2e544a3b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 12:00:16 -0800 Subject: [PATCH 1026/1066] Strict null check more files related to quick open --- src/tsconfig.strictNullChecks.json | 8 ++++ .../parts/quickopen/browser/quickOpenModel.ts | 2 +- src/vs/workbench/browser/quickopen.ts | 6 +-- .../quickopen/browser/commandsHandler.ts | 18 ++++----- .../quickopen/browser/gotoLineHandler.ts | 38 +++++++++++-------- .../quickopen/browser/viewPickerHandler.ts | 4 +- .../parts/search/browser/openFileHandler.ts | 15 +++----- .../parts/search/browser/openSymbolHandler.ts | 10 ++--- .../terminal/browser/terminalQuickOpen.ts | 6 ++- .../welcome/overlay/browser/welcomeOverlay.ts | 2 +- 10 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 4d89719082c..f2ceaf10385 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -565,13 +565,18 @@ "./vs/workbench/parts/preferences/browser/settingsWidgets.ts", "./vs/workbench/parts/preferences/common/smartSnippetInserter.ts", "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", + "./vs/workbench/parts/quickopen/browser/commandsHandler.ts", + "./vs/workbench/parts/quickopen/browser/gotoLineHandler.ts", "./vs/workbench/parts/quickopen/browser/helpHandler.ts", + "./vs/workbench/parts/quickopen/browser/viewPickerHandler.ts", "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", "./vs/workbench/parts/scm/common/scm.ts", "./vs/workbench/parts/scm/electron-browser/dirtydiffDecorator.ts", "./vs/workbench/parts/scm/electron-browser/scmActivity.ts", "./vs/workbench/parts/scm/electron-browser/scmMenus.ts", "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", + "./vs/workbench/parts/search/browser/openFileHandler.ts", + "./vs/workbench/parts/search/browser/openSymbolHandler.ts", "./vs/workbench/parts/search/browser/patternInputWidget.ts", "./vs/workbench/parts/search/browser/replaceContributions.ts", "./vs/workbench/parts/search/browser/replaceService.ts", @@ -581,6 +586,7 @@ "./vs/workbench/parts/search/common/search.ts", "./vs/workbench/parts/search/common/searchModel.ts", "./vs/workbench/parts/search/test/browser/mockSearchTree.ts", + "./vs/workbench/parts/search/test/browser/openFileHandler.test.ts", "./vs/workbench/parts/search/test/common/searchModel.test.ts", "./vs/workbench/parts/search/test/common/searchResult.test.ts", "./vs/workbench/parts/snippets/electron-browser/configureSnippets.ts", @@ -619,6 +625,7 @@ "./vs/workbench/parts/tasks/test/common/problemMatcher.test.ts", "./vs/workbench/parts/tasks/test/electron-browser/configuration.test.ts", "./vs/workbench/parts/terminal/browser/terminalFindWidget.ts", + "./vs/workbench/parts/terminal/browser/terminalQuickOpen.ts", "./vs/workbench/parts/terminal/browser/terminalTab.ts", "./vs/workbench/parts/terminal/browser/terminalWidgetManager.ts", "./vs/workbench/parts/terminal/common/terminal.ts", @@ -648,6 +655,7 @@ "./vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.ts", "./vs/workbench/parts/welcome/gettingStarted/electron-browser/telemetryOptOut.ts", "./vs/workbench/parts/welcome/gettingStarted/test/common/gettingStarted.test.ts", + "./vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts", "./vs/workbench/parts/welcome/page/electron-browser/vs_code_welcome_page.ts", "./vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution.ts", "./vs/workbench/parts/welcome/page/electron-browser/welcomePage.ts", diff --git a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts index 7acd2cbbb9b..907b9e8e7e0 100644 --- a/src/vs/base/parts/quickopen/browser/quickOpenModel.ts +++ b/src/vs/base/parts/quickopen/browser/quickOpenModel.ts @@ -214,7 +214,7 @@ export class QuickOpenEntryGroup extends QuickOpenEntry { return this.groupLabel; } - setGroupLabel(groupLabel: string): void { + setGroupLabel(groupLabel: string | undefined): void { this.groupLabel = groupLabel; } diff --git a/src/vs/workbench/browser/quickopen.ts b/src/vs/workbench/browser/quickopen.ts index af06b1fbade..843dce6d80f 100644 --- a/src/vs/workbench/browser/quickopen.ts +++ b/src/vs/workbench/browser/quickopen.ts @@ -50,14 +50,14 @@ export class QuickOpenHandler { /** * The ARIA label to apply when this quick open handler is active in quick open. */ - getAriaLabel() { + getAriaLabel(): string | null { return null; } /** * Extra CSS class name to add to the quick open widget to do custom styling of entries. */ - getClass() { + getClass(): string | null { return null; } @@ -102,7 +102,7 @@ export class QuickOpenHandler { /** * Allows to return a label that will be placed to the side of the results from this handler or null if none. */ - getGroupLabel() { + getGroupLabel(): string | null { return null; } diff --git a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts index d86c5ac4cfd..13019490a5b 100644 --- a/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/commandsHandler.ts @@ -91,7 +91,7 @@ class CommandsHistory extends Disposable { private load(): void { const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE, StorageScope.GLOBAL); - let serializedCache: ISerializedCommandHistory; + let serializedCache: ISerializedCommandHistory | undefined; if (raw) { try { serializedCache = JSON.parse(raw); @@ -118,7 +118,7 @@ class CommandsHistory extends Disposable { commandHistory.set(commandId, commandCounter++); // set counter to command } - peek(commandId: string): number { + peek(commandId: string): number | undefined { return commandHistory.peek(commandId); } @@ -214,14 +214,14 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { private description: string; private alias: string; private labelLowercase: string; - private keybindingAriaLabel: string; + private readonly keybindingAriaLabel?: string; constructor( private commandId: string, private keybinding: ResolvedKeybinding, private label: string, alias: string, - highlights: { label: IHighlight[], alias: IHighlight[] }, + highlights: { label: IHighlight[], alias?: IHighlight[] }, private onBeforeRun: (commandId: string) => void, @INotificationService private readonly notificationService: INotificationService, @ITelemetryService protected telemetryService: ITelemetryService @@ -229,15 +229,15 @@ abstract class BaseCommandEntry extends QuickOpenEntryGroup { super(); this.labelLowercase = this.label.toLowerCase(); - this.keybindingAriaLabel = keybinding ? keybinding.getAriaLabel() : undefined; + this.keybindingAriaLabel = keybinding ? keybinding.getAriaLabel() || undefined : undefined; if (this.label !== alias) { this.alias = alias; } else { - highlights.alias = null; + highlights.alias = undefined; } - this.setHighlights(highlights.label, null, highlights.alias); + this.setHighlights(highlights.label, undefined, highlights.alias); } getCommandId(): string { @@ -541,7 +541,7 @@ export class CommandsHandler extends QuickOpenHandler { // Add an 'alias' in original language when running in different locale const aliasTitle = (language !== LANGUAGE_DEFAULT && typeof action.item.title !== 'string') ? action.item.title.original : null; - const aliasCategory = (language !== LANGUAGE_DEFAULT && category && typeof action.item.category !== 'string') ? action.item.category.original : null; + const aliasCategory = (language !== LANGUAGE_DEFAULT && category && action.item.category && typeof action.item.category !== 'string') ? action.item.category.original : null; let alias; if (aliasTitle && category) { alias = aliasCategory ? `${aliasCategory}: ${aliasTitle}` : `${category}: ${aliasTitle}`; @@ -560,7 +560,7 @@ export class CommandsHandler extends QuickOpenHandler { } getAutoFocus(searchValue: string, context: { model: IModel, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus { - let autoFocusPrefixMatch = searchValue.trim(); + let autoFocusPrefixMatch: string | undefined = searchValue.trim(); if (autoFocusPrefixMatch && this.commandHistoryEnabled) { const firstEntry = context.model && context.model.entries[0]; diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index a29d34e5c9c..4db21cc95fd 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -61,7 +61,7 @@ export class GotoLineAction extends QuickOpenAction { if (restoreOptions) { Event.once(this._quickOpenService.onHide)(() => { - activeTextEditorWidget.updateOptions(restoreOptions); + activeTextEditorWidget.updateOptions(restoreOptions!); }); } @@ -93,13 +93,16 @@ class GotoLineEntry extends EditorQuickOpenEntry { const maxLineNumber = this.getMaxLineNumber(); if (this.invalidRange(maxLineNumber)) { - const currentLine = this.editorService.activeTextEditorWidget.getPosition().lineNumber; + const position = this.editorService.activeTextEditorWidget.getPosition(); + if (position) { + const currentLine = position.lineNumber; - if (maxLineNumber > 0) { - return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber); + if (maxLineNumber > 0) { + return nls.localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}. Type a line number between 1 and {1} to navigate to.", currentLine, maxLineNumber); + } + + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); } - - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); } // Input valid, indicate action @@ -181,7 +184,7 @@ class GotoLineEntry extends EditorQuickOpenEntry { // Decorate if possible if (types.isFunction(activeTextEditorWidget.changeDecorations)) { - this.handler.decorateOutline(range, activeTextEditorWidget, this.editorService.activeControl.group); + this.handler.decorateOutline(range, activeTextEditorWidget, this.editorService.activeControl.group!); } } @@ -208,8 +211,8 @@ export class GotoLineHandler extends QuickOpenHandler { static readonly ID = 'workbench.picker.line'; - private rangeHighlightDecorationId: IEditorLineDecoration; - private lastKnownEditorViewState: IEditorViewState; + private rangeHighlightDecorationId: IEditorLineDecoration | null; + private lastKnownEditorViewState: IEditorViewState | null; constructor(@IEditorService private readonly editorService: IEditorService) { super(); @@ -217,9 +220,11 @@ export class GotoLineHandler extends QuickOpenHandler { getAriaLabel(): string { if (this.editorService.activeTextEditorWidget) { - const currentLine = this.editorService.activeTextEditorWidget.getPosition().lineNumber; - - return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + const position = this.editorService.activeTextEditorWidget.getPosition(); + if (position) { + const currentLine = position.lineNumber; + return nls.localize('gotoLineLabelEmpty', "Current Line: {0}. Type a line number to navigate to.", currentLine); + } } return nls.localize('cannotRunGotoLine', "Open a text file first to go to a line."); @@ -288,14 +293,15 @@ export class GotoLineHandler extends QuickOpenHandler { } clearDecorations(): void { - if (this.rangeHighlightDecorationId) { + const rangeHighlightDecorationId = this.rangeHighlightDecorationId; + if (rangeHighlightDecorationId) { this.editorService.visibleControls.forEach(editor => { - if (editor.group.id === this.rangeHighlightDecorationId.groupId) { + if (editor.group && editor.group.id === rangeHighlightDecorationId.groupId) { const editorControl = editor.getControl(); editorControl.changeDecorations(changeAccessor => { changeAccessor.deltaDecorations([ - this.rangeHighlightDecorationId.lineDecorationId, - this.rangeHighlightDecorationId.rangeHighlightId + rangeHighlightDecorationId.lineDecorationId, + rangeHighlightDecorationId.rangeHighlightId ], []); }); } diff --git a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts index 5c6b0713db8..fe8cccaf627 100644 --- a/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/viewPickerHandler.ts @@ -137,7 +137,7 @@ export class ViewPickerHandler extends QuickOpenHandler { const result: ViewEntry[] = []; if (views.length) { for (const view of views) { - if (this.contextKeyService.contextMatchesRules(view.when)) { + if (this.contextKeyService.contextMatchesRules(view.when || null)) { result.push(new ViewEntry(view.name, viewlet.name, () => this.viewsService.openView(view.id, true))); } } @@ -155,7 +155,7 @@ export class ViewPickerHandler extends QuickOpenHandler { // Viewlet Views viewlets.forEach((viewlet, index) => { - const viewContainer: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(viewlet.id); + const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(viewlet.id); if (viewContainer) { const viewEntriesForViewlet: ViewEntry[] = getViewEntriesForViewlet(viewlet, viewContainer); viewEntries.push(...viewEntriesForViewlet); diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index ff7a3ec092b..5e5566fd385 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -97,14 +97,11 @@ export class FileEntry extends EditorQuickOpenEntry { const input: IResourceInput = { resource: this.resource, options: { - pinned: !this.configurationService.getValue().workbench.editor.enablePreviewFromQuickOpen + pinned: !this.configurationService.getValue().workbench.editor.enablePreviewFromQuickOpen, + selection: this.range ? this.range : undefined } }; - if (this.range) { - input.options.selection = this.range; - } - return input; } } @@ -178,7 +175,7 @@ export class OpenFileHandler extends QuickOpenHandler { for (const fileMatch of complete.results) { const label = paths.basename(fileMatch.resource.fsPath); - const description = this.labelService.getUriLabel(resources.dirname(fileMatch.resource), { relative: true }); + const description = this.labelService.getUriLabel(resources.dirname(fileMatch.resource)!, { relative: true }); results.push(this.instantiationService.createInstance(FileEntry, fileMatch.resource, label, description, iconClass)); } @@ -188,14 +185,14 @@ export class OpenFileHandler extends QuickOpenHandler { }); } - private getAbsolutePathResult(query: IPreparedQuery): Promise { + private getAbsolutePathResult(query: IPreparedQuery): Promise { if (paths.isAbsolute(query.original)) { const resource = URI.file(query.original); return this.fileService.resolveFile(resource).then(stat => stat.isDirectory ? undefined : resource, error => undefined); } - return Promise.resolve(null); + return Promise.resolve(undefined); } private doResolveQueryOptions(query: IPreparedQuery, cacheKey?: string, maxSortedResults?: number): IFileQueryBuilderOptions { @@ -273,7 +270,7 @@ export class CacheState { private loadingPhase = LoadingPhase.Created; private promise: Promise; - constructor(cacheQuery: (cacheKey: string) => IFileQuery, private doLoad: (query: IFileQuery) => Promise, private doDispose: (cacheKey: string) => Promise, private previous: CacheState) { + constructor(cacheQuery: (cacheKey: string) => IFileQuery, private doLoad: (query: IFileQuery) => Promise, private doDispose: (cacheKey: string) => Promise, private previous: CacheState | null) { this.query = cacheQuery(this._cacheKey); if (this.previous) { const current = objects.assign({}, this.query, { cacheKey: null }); diff --git a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts index bcc785e66b8..c57bea9b782 100644 --- a/src/vs/workbench/parts/search/browser/openSymbolHandler.ts +++ b/src/vs/workbench/parts/search/browser/openSymbolHandler.ts @@ -27,7 +27,7 @@ import { Schemas } from 'vs/base/common/network'; import { IOpenerService } from 'vs/platform/opener/common/opener'; class SymbolEntry extends EditorQuickOpenEntry { - private bearingResolve: Promise; + private bearingResolve: Promise; constructor( private bearing: IWorkspaceSymbol, @@ -48,7 +48,7 @@ class SymbolEntry extends EditorQuickOpenEntry { return nls.localize('entryAriaLabel', "{0}, symbols picker", this.getLabel()); } - getDescription(): string { + getDescription(): string | null { const containerName = this.bearing.containerName; if (this.bearing.location.uri) { if (containerName) { @@ -58,7 +58,7 @@ class SymbolEntry extends EditorQuickOpenEntry { return this.labelService.getUriLabel(this.bearing.location.uri, { relative: true }); } - return containerName; + return containerName || null; } getIcon(): string { @@ -105,7 +105,7 @@ class SymbolEntry extends EditorQuickOpenEntry { }; if (this.bearing.location.range) { - input.options.selection = Range.collapseToStart(this.bearing.location.range); + input.options!.selection = Range.collapseToStart(this.bearing.location.range); } return input; @@ -206,7 +206,7 @@ export class OpenSymbolHandler extends QuickOpenHandler { } const entry = this.instantiationService.createInstance(SymbolEntry, element, provider); - entry.setHighlights(filters.matchesFuzzy2(searchValue, entry.getLabel())); + entry.setHighlights(filters.matchesFuzzy2(searchValue, entry.getLabel()) || []); bucket.push(entry); } } diff --git a/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts index 1b8f9848510..a2000fb85fb 100644 --- a/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts +++ b/src/vs/workbench/parts/terminal/browser/terminalQuickOpen.ts @@ -95,7 +95,11 @@ export class TerminalPickerHandler extends QuickOpenHandler { return true; } - const highlights = matchesFuzzy(normalizedSearchValueLowercase, e.getLabel(), true); + const label = e.getLabel(); + if (!label) { + return false; + } + const highlights = matchesFuzzy(normalizedSearchValueLowercase, label, true); if (!highlights) { return false; } diff --git a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts index e44c0f8d11f..a7a6c4054f5 100644 --- a/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/parts/welcome/overlay/browser/welcomeOverlay.ts @@ -171,7 +171,7 @@ class WelcomeOverlay { const container = this.partService.getContainer(Parts.EDITOR_PART); const offset = this.partService.getTitleBarOffset(); - this._overlay = dom.append(container.parentElement, $('.welcomeOverlay')); + this._overlay = dom.append(container.parentElement!, $('.welcomeOverlay')); this._overlay.style.top = `${offset}px`; this._overlay.style.height = `calc(100% - ${offset}px)`; this._overlay.style.display = 'none'; From 6e2995e40c3e3ef1e63ff519c8da88f5d586a2a6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 14:06:08 -0800 Subject: [PATCH 1027/1066] More work strict null checking quick open related files --- src/tsconfig.strictNullChecks.json | 2 ++ .../browser/parts/quickinput/quickInput.ts | 2 +- .../parts/quickopen/browser/gotoSymbolHandler.ts | 4 ++-- .../parts/search/browser/openAnythingHandler.ts | 2 +- src/vs/workbench/parts/tasks/browser/quickOpen.ts | 15 +++++++++------ .../parts/tasks/browser/taskQuickOpen.ts | 2 +- .../common/configurationResolver.ts | 6 +++--- .../node/variableResolver.ts | 8 ++++---- 8 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index f2ceaf10385..298d66413ce 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -606,6 +606,8 @@ "./vs/workbench/parts/stats/test/workspaceStats.test.ts", "./vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution.ts", "./vs/workbench/parts/surveys/electron-browser/nps.contribution.ts", + "./vs/workbench/parts/tasks/browser/quickOpen.ts", + "./vs/workbench/parts/tasks/browser/taskQuickOpen.ts", "./vs/workbench/parts/tasks/common/problemCollectors.ts", "./vs/workbench/parts/tasks/common/problemMatcher.ts", "./vs/workbench/parts/tasks/common/taskDefinitionRegistry.ts", diff --git a/src/vs/workbench/browser/parts/quickinput/quickInput.ts b/src/vs/workbench/browser/parts/quickinput/quickInput.ts index 9926fb1de1f..1816f6118ff 100644 --- a/src/vs/workbench/browser/parts/quickinput/quickInput.ts +++ b/src/vs/workbench/browser/parts/quickinput/quickInput.ts @@ -115,7 +115,7 @@ class QuickInput implements IQuickInput { this.onDidHideEmitter, ]; - private busyDelay: TimeoutTimer; + private busyDelay: TimeoutTimer | null; constructor(protected ui: QuickInputUI) { } diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 4e161da020a..dfab2401a49 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -82,9 +82,9 @@ class OutlineModel extends QuickOpenModel { this.entries.forEach((entry: SymbolEntry) => { // Clear all state first - entry.setGroupLabel(null); + entry.setGroupLabel(undefined); entry.setShowBorder(false); - entry.setHighlights(null); + entry.setHighlights([]); entry.setHidden(false); // Filter by search diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 7ff26d8ff63..6318be52a6a 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -120,7 +120,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { } // Combine results. - const mergedResults: QuickOpenEntry[] = [].concat(...results.map(r => r.entries)); + const mergedResults: QuickOpenEntry[] = ([] as QuickOpenEntry[]).concat(...results.map(r => r.entries)); // Sort const compare = (elementA: QuickOpenEntry, elementB: QuickOpenEntry) => compareItemsByScore(elementA, elementB, query, true, QuickOpenItemAccessor, this.scorerCache); diff --git a/src/vs/workbench/parts/tasks/browser/quickOpen.ts b/src/vs/workbench/parts/tasks/browser/quickOpen.ts index fb5f8c33e84..ab7796f27a5 100644 --- a/src/vs/workbench/parts/tasks/browser/quickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/quickOpen.ts @@ -28,7 +28,7 @@ export class TaskEntry extends Model.QuickOpenEntry { return this.task._label; } - public getDescription(): string { + public getDescription(): string | null { if (!this.taskService.needsFolderQualification()) { return null; } @@ -51,7 +51,7 @@ export class TaskEntry extends Model.QuickOpenEntry { this.taskService.run(task, options).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here }); - if (!task.command || task.command.presentation.focus) { + if (!task.command || (task.command.presentation && task.command.presentation.focus)) { this.quickOpenService.close(); return false; } @@ -67,7 +67,7 @@ export class TaskGroupEntry extends Model.QuickOpenEntryGroup { export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler { - private tasks: Promise>; + private tasks?: Promise>; constructor( protected quickOpenService: IQuickOpenService, @@ -87,7 +87,10 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler { this.tasks = undefined; } - public getResults(input: string, token: CancellationToken): Promise { + public getResults(input: string, token: CancellationToken): Promise { + if (!this.tasks) { + return Promise.resolve(null); + } return this.tasks.then((tasks) => { let entries: Model.QuickOpenEntry[] = []; if (tasks.length === 0 || token.isCancellationRequested) { @@ -186,7 +189,7 @@ class CustomizeTaskAction extends Action { } } - private getTask(element: any): CustomTask | ContributedTask { + private getTask(element: any): CustomTask | ContributedTask | undefined { if (element instanceof TaskEntry) { return element.task; } else if (element instanceof TaskGroupEntry) { @@ -220,7 +223,7 @@ export class QuickOpenActionContributor extends ActionBarContributor { return actions; } - private getTask(context: any): CustomTask | ContributedTask { + private getTask(context: any): CustomTask | ContributedTask | undefined { if (!context) { return undefined; } diff --git a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts index 16ea1b58b0c..289ef28e7d1 100644 --- a/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts +++ b/src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts @@ -19,7 +19,7 @@ class TaskEntry extends base.TaskEntry { super(quickOpenService, taskService, task, highlights); } - public run(mode: QuickOpen.Mode, context: Model.IContext): boolean { + public run(mode: QuickOpen.Mode, context: QuickOpen.IEntryRunContext): boolean { if (mode === QuickOpen.Mode.PREVIEW) { return false; } diff --git a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts index d1004cf5a04..d6993e1b41b 100644 --- a/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/configurationResolver.ts @@ -12,9 +12,9 @@ export const IConfigurationResolverService = createDecorator): IStringDictionary; + resolve(folder: IWorkspaceFolder | undefined, value: string): string; + resolve(folder: IWorkspaceFolder | undefined, value: string[]): string[]; + resolve(folder: IWorkspaceFolder | undefined, value: IStringDictionary): IStringDictionary; /** * Recursively resolves all variables in the given config and returns a copy of it with substituted values. diff --git a/src/vs/workbench/services/configurationResolver/node/variableResolver.ts b/src/vs/workbench/services/configurationResolver/node/variableResolver.ts index af4d991f3d3..7e86f0e4185 100644 --- a/src/vs/workbench/services/configurationResolver/node/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/node/variableResolver.ts @@ -43,10 +43,10 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe } } - public resolve(root: IWorkspaceFolder, value: string): string; - public resolve(root: IWorkspaceFolder, value: string[]): string[]; - public resolve(root: IWorkspaceFolder, value: IStringDictionary): IStringDictionary; - public resolve(root: IWorkspaceFolder, value: any): any { + public resolve(root: IWorkspaceFolder | undefined, value: string): string; + public resolve(root: IWorkspaceFolder | undefined, value: string[]): string[]; + public resolve(root: IWorkspaceFolder | undefined, value: IStringDictionary): IStringDictionary; + public resolve(root: IWorkspaceFolder | undefined, value: any): any { return this.recursiveResolve(root ? root.uri : undefined, value); } From 34dbc7195c5d8cb76a5066c74b73ef2713be50ce Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Tue, 5 Feb 2019 14:16:02 -0800 Subject: [PATCH 1028/1066] Fix strict null checks --- .../parts/terminal/electron-browser/terminalConfigHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts index 9aee7905251..10bb8f9723e 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalConfigHelper.ts @@ -199,7 +199,7 @@ export class TerminalConfigHelper implements ITerminalConfigHelper { } let envString: string | undefined; if (envConfigValue.workspace) { - envString = `env: {${Object.keys(envConfigValue.workspace).map(k => `${k}:${envConfigValue.workspace[k]}`).join(', ')}}`; + envString = `env: {${Object.keys(envConfigValue.workspace).map(k => `${k}:${envConfigValue.workspace![k]}`).join(', ')}}`; } // Should not be localized as it's json-like syntax referencing settings keys const workspaceConfigStrings: string[] = []; From 8446e3fb19cc38dee87a718052960fcb5cecb4c6 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 14:28:27 -0800 Subject: [PATCH 1029/1066] Strict null check more quick open files --- src/tsconfig.strictNullChecks.json | 4 ++ .../standalone/browser/quickOpen/gotoLine.ts | 23 +++++----- .../browser/parts/editor/editorPicker.ts | 11 +++-- .../quickopen/browser/gotoSymbolHandler.ts | 45 ++++++++++--------- .../search/browser/openAnythingHandler.ts | 6 +-- .../parts/search/browser/openFileHandler.ts | 4 +- 6 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 298d66413ce..9c4563e91f7 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -155,6 +155,7 @@ "./vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.ts", "./vs/editor/standalone/browser/inspectTokens/inspectTokens.ts", "./vs/editor/standalone/browser/quickOpen/editorQuickOpen.ts", + "./vs/editor/standalone/browser/quickOpen/gotoLine.ts", "./vs/editor/standalone/browser/quickOpen/quickCommand.ts", "./vs/editor/standalone/browser/quickOpen/quickOpenEditorWidget.ts", "./vs/editor/standalone/browser/quickOpen/quickOutline.ts", @@ -434,6 +435,7 @@ "./vs/workbench/browser/parts/editor/breadcrumbsModel.ts", "./vs/workbench/browser/parts/editor/editor.ts", "./vs/workbench/browser/parts/editor/editorControl.ts", + "./vs/workbench/browser/parts/editor/editorPicker.ts", "./vs/workbench/browser/parts/editor/editorWidgets.ts", "./vs/workbench/browser/parts/editor/rangeDecorations.ts", "./vs/workbench/browser/parts/editor/resourceViewer.ts", @@ -567,6 +569,7 @@ "./vs/workbench/parts/preferences/test/common/smartSnippetInserter.test.ts", "./vs/workbench/parts/quickopen/browser/commandsHandler.ts", "./vs/workbench/parts/quickopen/browser/gotoLineHandler.ts", + "./vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts", "./vs/workbench/parts/quickopen/browser/helpHandler.ts", "./vs/workbench/parts/quickopen/browser/viewPickerHandler.ts", "./vs/workbench/parts/relauncher/electron-browser/relauncher.contribution.ts", @@ -575,6 +578,7 @@ "./vs/workbench/parts/scm/electron-browser/scmActivity.ts", "./vs/workbench/parts/scm/electron-browser/scmMenus.ts", "./vs/workbench/parts/scm/electron-browser/scmUtil.ts", + "./vs/workbench/parts/search/browser/openAnythingHandler.ts", "./vs/workbench/parts/search/browser/openFileHandler.ts", "./vs/workbench/parts/search/browser/openSymbolHandler.ts", "./vs/workbench/parts/search/browser/patternInputWidget.ts", diff --git a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts index 7c55710e9a2..47cc1ea540d 100644 --- a/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts +++ b/src/vs/editor/standalone/browser/quickOpen/gotoLine.ts @@ -6,8 +6,8 @@ import 'vs/css!./gotoLine'; import * as nls from 'vs/nls'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IContext, QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; -import { IAutoFocus, Mode } from 'vs/base/parts/quickopen/common/quickOpen'; +import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; +import { IAutoFocus, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen'; import { ICodeEditor, IDiffEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ServicesAccessor, registerEditorAction } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; @@ -49,14 +49,15 @@ export class GotoLineEntry extends QuickOpenEntry { position = new Position(numbers[0], numbers[1]); } - let model: ITextModel; + let model: ITextModel | null; if (isCodeEditor(this.editor)) { model = this.editor.getModel(); } else { - model = (this.editor).getModel().modified; + const diffModel = (this.editor).getModel(); + model = diffModel ? diffModel.modified : null; } - const isValid = model.validatePosition(position).equals(position); + const isValid = model ? model.validatePosition(position).equals(position) : false; let label: string; if (isValid) { @@ -65,10 +66,10 @@ export class GotoLineEntry extends QuickOpenEntry { } else { label = nls.localize('gotoLineLabelValidLine', "Go to line {0}", position.lineNumber, position.column); } - } else if (position.lineNumber < 1 || position.lineNumber > model.getLineCount()) { - label = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to", model.getLineCount()); + } else if (position.lineNumber < 1 || position.lineNumber > (model ? model.getLineCount() : 0)) { + label = nls.localize('gotoLineLabelEmptyWithLineLimit', "Type a line number between 1 and {0} to navigate to", model ? model.getLineCount() : 0); } else { - label = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to", model.getLineMaxColumn(position.lineNumber)); + label = nls.localize('gotoLineLabelEmptyWithLineAndColumnLimit', "Type a character between 1 and {0} to navigate to", model ? model.getLineMaxColumn(position.lineNumber) : 0); } return { @@ -83,12 +84,12 @@ export class GotoLineEntry extends QuickOpenEntry { } getAriaLabel(): string { - const currentLine = this.editor.getPosition().lineNumber; - + const position = this.editor.getPosition(); + const currentLine = position ? position.lineNumber : 0; return nls.localize('gotoLineAriaLabel', "Current Line: {0}. Go to line {0}.", currentLine, this.parseResult.label); } - run(mode: Mode, context: IContext): boolean { + run(mode: Mode, _context: IEntryRunContext): boolean { if (mode === Mode.OPEN) { return this.runOpen(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPicker.ts b/src/vs/workbench/browser/parts/editor/editorPicker.ts index fbbf6c9ef05..bdb719d39c0 100644 --- a/src/vs/workbench/browser/parts/editor/editorPicker.ts +++ b/src/vs/workbench/browser/parts/editor/editorPicker.ts @@ -5,7 +5,6 @@ import 'vs/css!./media/editorpicker'; import * as nls from 'vs/nls'; -import { URI } from 'vs/base/common/uri'; import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel'; @@ -33,12 +32,12 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { getLabelOptions(): IIconLabelValueOptions { return { - extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()), + extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource() || undefined), italic: !this._group.isPinned(this.editor) }; } - getLabel(): string { + getLabel() { return this.editor.getName(); } @@ -50,7 +49,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { return this._group; } - getResource(): URI { + getResource() { return toResource(this.editor, { supportSideBySide: true }); } @@ -58,7 +57,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup { return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel()); } - getDescription(): string { + getDescription() { return this.editor.getDescription(); } @@ -109,7 +108,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler { return false; } - e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch); + e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch); return true; }); diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index dfab2401a49..d9bc4a3df3f 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -128,7 +128,7 @@ class OutlineModel extends QuickOpenModel { // Update previous result with count if (currentResult) { - currentResult.setGroupLabel(this.renderGroupLabel(currentType, typeCounter)); + currentResult.setGroupLabel(typeof currentType === 'number' ? this.renderGroupLabel(currentType, typeCounter) : undefined); } currentType = result.getKind(); @@ -146,7 +146,7 @@ class OutlineModel extends QuickOpenModel { // Update previous result with count if (currentResult) { - currentResult.setGroupLabel(this.renderGroupLabel(currentType, typeCounter)); + currentResult.setGroupLabel(typeof currentType === 'number' ? this.renderGroupLabel(currentType, typeCounter) : undefined); } } @@ -338,7 +338,7 @@ class SymbolEntry extends EditorQuickOpenEntryGroup { // Decorate if possible if (types.isFunction(activeTextEditorWidget.changeDecorations)) { - this.handler.decorateOutline(this.range, range, activeTextEditorWidget, this.editorService.activeControl.group); + this.handler.decorateOutline(this.range, range, activeTextEditorWidget, this.editorService.activeControl.group!); } } @@ -365,11 +365,11 @@ export class GotoSymbolHandler extends QuickOpenHandler { static readonly ID = 'workbench.picker.filesymbols'; - private rangeHighlightDecorationId: IEditorLineDecoration; - private lastKnownEditorViewState: IEditorViewState; + private rangeHighlightDecorationId?: IEditorLineDecoration; + private lastKnownEditorViewState: IEditorViewState | null; - private cachedOutlineRequest: Promise; - private pendingOutlineRequest: CancellationTokenSource; + private cachedOutlineRequest?: Promise; + private pendingOutlineRequest?: CancellationTokenSource; constructor( @IEditorService private readonly editorService: IEditorService @@ -386,11 +386,11 @@ export class GotoSymbolHandler extends QuickOpenHandler { private onDidActiveEditorChange(): void { this.clearOutlineRequest(); - this.lastKnownEditorViewState = undefined; + this.lastKnownEditorViewState = null; this.rangeHighlightDecorationId = undefined; } - getResults(searchValue: string, token: CancellationToken): Promise { + getResults(searchValue: string, token: CancellationToken): Promise { searchValue = searchValue.trim(); // Support to cancel pending outline requests @@ -406,6 +406,10 @@ export class GotoSymbolHandler extends QuickOpenHandler { // Resolve Outline Model return this.getOutline().then(outline => { + if (!outline) { + return outline; + } + if (token.isCancellationRequested) { return outline; } @@ -469,20 +473,20 @@ export class GotoSymbolHandler extends QuickOpenHandler { const label = strings.trim(element.name); // Show parent scope as description - const description: string = element.containerName; + const description = element.containerName || ''; const icon = symbolKindToCssClass(element.kind); // Add results.push(new SymbolEntry(i, label, element.kind, description, `symbol-icon ${icon}`, - element.range, element.selectionRange, null, this.editorService, this + element.range, element.selectionRange, [], this.editorService, this )); } return results; } - private getOutline(): Promise { + private getOutline(): Promise { if (!this.cachedOutlineRequest) { this.cachedOutlineRequest = this.doGetActiveOutline(); } @@ -499,7 +503,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { } if (model && types.isFunction((model).getLanguageIdentifier)) { - return Promise.resolve(asPromise(() => getDocumentSymbols(model, true, this.pendingOutlineRequest.token)).then(entries => { + return Promise.resolve(asPromise(() => getDocumentSymbols(model, true, this.pendingOutlineRequest!.token)).then(entries => { return new OutlineModel(this.toQuickOpenEntries(entries)); })); } @@ -515,7 +519,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { if (this.rangeHighlightDecorationId) { deleteDecorations.push(this.rangeHighlightDecorationId.lineDecorationId); deleteDecorations.push(this.rangeHighlightDecorationId.rangeHighlightId); - this.rangeHighlightDecorationId = null; + this.rangeHighlightDecorationId = undefined; } const newDecorations: IModelDeltaDecoration[] = [ @@ -555,20 +559,21 @@ export class GotoSymbolHandler extends QuickOpenHandler { } private clearDecorations(): void { - if (this.rangeHighlightDecorationId) { + const rangeHighlightDecorationId = this.rangeHighlightDecorationId; + if (rangeHighlightDecorationId) { this.editorService.visibleControls.forEach(editor => { - if (editor.group.id === this.rangeHighlightDecorationId.groupId) { + if (editor.group && editor.group.id === rangeHighlightDecorationId.groupId) { const editorControl = editor.getControl(); editorControl.changeDecorations((changeAccessor: IModelDecorationsChangeAccessor) => { changeAccessor.deltaDecorations([ - this.rangeHighlightDecorationId.lineDecorationId, - this.rangeHighlightDecorationId.rangeHighlightId + rangeHighlightDecorationId.lineDecorationId, + rangeHighlightDecorationId.rangeHighlightId ], []); }); } }); - this.rangeHighlightDecorationId = null; + this.rangeHighlightDecorationId = undefined; } } @@ -598,6 +603,6 @@ export class GotoSymbolHandler extends QuickOpenHandler { this.pendingOutlineRequest = undefined; } - this.cachedOutlineRequest = null; + this.cachedOutlineRequest = undefined; } } diff --git a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts index 6318be52a6a..a20fe2e6f8b 100644 --- a/src/vs/workbench/parts/search/browser/openAnythingHandler.ts +++ b/src/vs/workbench/parts/search/browser/openAnythingHandler.ts @@ -132,7 +132,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { entry.setRange(searchWithRange ? searchWithRange.range : null); const itemScore = scoreItem(entry, query, true, QuickOpenItemAccessor, this.scorerCache); - entry.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch); + entry.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch); } }); @@ -165,7 +165,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { return this.openFileHandler.hasShortResponseTime() && this.openSymbolHandler.hasShortResponseTime(); } - private extractRange(value: string): ISearchWithRange { + private extractRange(value: string): ISearchWithRange | null { if (!value) { return null; } @@ -211,7 +211,7 @@ export class OpenAnythingHandler extends QuickOpenHandler { } } - if (range) { + if (patternMatch && range) { return { search: value.substr(0, patternMatch.index), // clear range suffix from search value range: range diff --git a/src/vs/workbench/parts/search/browser/openFileHandler.ts b/src/vs/workbench/parts/search/browser/openFileHandler.ts index 5e5566fd385..2d52f6f68b5 100644 --- a/src/vs/workbench/parts/search/browser/openFileHandler.ts +++ b/src/vs/workbench/parts/search/browser/openFileHandler.ts @@ -43,7 +43,7 @@ export class FileQuickOpenModel extends QuickOpenModel { } export class FileEntry extends EditorQuickOpenEntry { - private range: IRange; + private range: IRange | null; constructor( private resource: URI, @@ -85,7 +85,7 @@ export class FileEntry extends EditorQuickOpenEntry { return this.resource; } - setRange(range: IRange): void { + setRange(range: IRange | null): void { this.range = range; } From e82d8bb6e6c8fd07ca16eacd16663ebd221187cb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 14:56:10 -0800 Subject: [PATCH 1030/1066] Strict null checking goToDefinition --- src/vs/editor/browser/editorExtensions.ts | 6 +-- .../goToDefinition/goToDefinitionCommands.ts | 41 +++++++++++-------- .../goToDefinition/goToDefinitionMouse.ts | 23 ++++++----- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 15365b311a9..b7061803c01 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -184,9 +184,9 @@ export interface IActionOptions extends ICommandOptions { } export abstract class EditorAction extends EditorCommand { - public label: string; - public alias: string; - private menuOpts: IEditorCommandMenuOptions | undefined; + public readonly label: string; + public readonly alias: string; + private readonly menuOpts: IEditorCommandMenuOptions | undefined; constructor(opts: IActionOptions) { super(opts); diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts index 52648d04694..4f99a51c6a6 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionCommands.ts @@ -51,6 +51,9 @@ export class DefinitionAction extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor): Promise { + if (!editor.hasModel()) { + return Promise.resolve(undefined); + } const notificationService = accessor.get(INotificationService); const editorService = accessor.get(ICodeEditorService); const progressService = accessor.get(IProgressService); @@ -112,14 +115,14 @@ export class DefinitionAction extends EditorAction { return getDefinitionsAtPosition(model, position, token); } - protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info && info.word ? nls.localize('noResultWord', "No definition found for '{0}'", info.word) : nls.localize('generic.noResults', "No definition found"); } protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 && nls.localize('meta.title', " – {0} definitions", model.references.length); + return model.references.length > 1 ? nls.localize('meta.title', " – {0} definitions", model.references.length) : ''; } private async _onResult(editorService: ICodeEditorService, editor: ICodeEditor, model: ReferencesModel): Promise { @@ -129,21 +132,23 @@ export class DefinitionAction extends EditorAction { if (this._configuration.openInPeek) { this._openInPeek(editorService, editor, model); - } else { + } else if (editor.hasModel()) { const next = model.nearestReference(editor.getModel().uri, editor.getPosition()); - const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide); - if (targetEditor && model.references.length > 1) { - this._openInPeek(editorService, targetEditor, model); - } else { - model.dispose(); + if (next) { + const targetEditor = await this._openReference(editor, editorService, next, this._configuration.openToSide); + if (targetEditor && model.references.length > 1) { + this._openInPeek(editorService, targetEditor, model); + } else { + model.dispose(); + } } } } - private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise { + private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: Location | LocationLink, sideBySide: boolean): Promise { // range is the target-selection-range when we have one // and the the fallback is the 'full' range - let range: IRange = undefined; + let range: IRange | undefined = undefined; if (isLocationLink(reference)) { range = reference.targetSelectionRange; } @@ -265,14 +270,14 @@ export class DeclarationAction extends DefinitionAction { return getDeclarationsAtPosition(model, position, token); } - protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info && info.word ? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word) : nls.localize('decl.generic.noResults', "No declaration found"); } protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length); + return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : ''; } } @@ -295,14 +300,14 @@ export class GoToDeclarationAction extends DeclarationAction { }); } - protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info && info.word ? nls.localize('decl.noResultWord', "No declaration found for '{0}'", info.word) : nls.localize('decl.generic.noResults', "No declaration found"); } protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 && nls.localize('decl.meta.title', " – {0} declarations", model.references.length); + return model.references.length > 1 ? nls.localize('decl.meta.title', " – {0} declarations", model.references.length) : ''; } } @@ -329,14 +334,14 @@ export class ImplementationAction extends DefinitionAction { return getImplementationsAtPosition(model, position, token); } - protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info && info.word ? nls.localize('goToImplementation.noResultWord', "No implementation found for '{0}'", info.word) : nls.localize('goToImplementation.generic.noResults', "No implementation found"); } protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 && nls.localize('meta.implementations.title', " – {0} implementations", model.references.length); + return model.references.length > 1 ? nls.localize('meta.implementations.title', " – {0} implementations", model.references.length) : ''; } } @@ -387,14 +392,14 @@ export class TypeDefinitionAction extends DefinitionAction { return getTypeDefinitionsAtPosition(model, position, token); } - protected _getNoResultFoundMessage(info?: IWordAtPosition): string { + protected _getNoResultFoundMessage(info: IWordAtPosition | null): string { return info && info.word ? nls.localize('goToTypeDefinition.noResultWord', "No type definition found for '{0}'", info.word) : nls.localize('goToTypeDefinition.generic.noResults', "No type definition found"); } protected _getMetaTitle(model: ReferencesModel): string { - return model.references.length > 1 && nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length); + return model.references.length > 1 ? nls.localize('meta.typeDefinitions.title', " – {0} type definitions", model.references.length) : ''; } } diff --git a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts index 55e2e6e0047..abb0f9c3575 100644 --- a/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts +++ b/src/vs/editor/contrib/goToDefinition/goToDefinitionMouse.ts @@ -34,8 +34,8 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC private editor: ICodeEditor; private toUnhook: IDisposable[]; private decorations: string[]; - private currentWordUnderMouse: IWordAtPosition; - private previousPromise: CancelablePromise; + private currentWordUnderMouse: IWordAtPosition | null; + private previousPromise: CancelablePromise | null; constructor( editor: ICodeEditor, @@ -51,7 +51,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC this.toUnhook.push(linkGesture); this.toUnhook.push(linkGesture.onMouseMoveOrRelevantKeyDown(([mouseEvent, keyboardEvent]) => { - this.startFindDefinition(mouseEvent, keyboardEvent); + this.startFindDefinition(mouseEvent, keyboardEvent || undefined); })); this.toUnhook.push(linkGesture.onExecute((mouseEvent: ClickLinkMouseEvent) => { @@ -79,20 +79,20 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return; } - if (!this.isEnabled(mouseEvent, withKey)) { + if (!this.editor.hasModel() || !this.isEnabled(mouseEvent, withKey)) { this.currentWordUnderMouse = null; this.removeDecorations(); return; } // Find word at mouse position - let position = mouseEvent.target.position; - let word = position ? this.editor.getModel().getWordAtPosition(position) : null; + const word = mouseEvent.target.position ? this.editor.getModel().getWordAtPosition(mouseEvent.target.position) : null; if (!word) { this.currentWordUnderMouse = null; this.removeDecorations(); return; } + const position = mouseEvent.target.position!; // Return early if word at position is still the same if (this.currentWordUnderMouse && this.currentWordUnderMouse.startColumn === word.startColumn && this.currentWordUnderMouse.endColumn === word.endColumn && this.currentWordUnderMouse.word === word.word) { @@ -158,9 +158,10 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); } + const modeId = this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath); this.addDecoration( wordRange, - new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilepathOrFirstLine(textEditorModel.uri.fsPath), previewValue) + new MarkdownString().appendCodeblock(modeId ? modeId : '', previewValue) ); ref.dispose(); }); @@ -274,10 +275,10 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC } private isEnabled(mouseEvent: ClickLinkMouseEvent, withKey?: ClickLinkKeyboardEvent): boolean { - return this.editor.getModel() && + return this.editor.hasModel() && mouseEvent.isNoneOrSingleMouseDown && (mouseEvent.target.type === MouseTargetType.CONTENT_TEXT) && - (mouseEvent.hasTriggerModifier || (withKey && withKey.keyCodeIsTriggerKey)) && + (mouseEvent.hasTriggerModifier || (withKey ? withKey.keyCodeIsTriggerKey : false)) && DefinitionProviderRegistry.has(this.editor.getModel()); } @@ -287,11 +288,11 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC return Promise.resolve(null); } - return getDefinitionsAtPosition(model, target.position, token); + return getDefinitionsAtPosition(model, target.position!, token); } private gotoDefinition(target: IMouseTarget, sideBySide: boolean): Promise { - this.editor.setPosition(target.position); + this.editor.setPosition(target.position!); const action = new DefinitionAction(new DefinitionActionConfig(sideBySide, false, true, false), { alias: undefined, label: undefined, id: undefined, precondition: undefined }); return this.editor.invokeWithinContext(accessor => action.run(accessor, this.editor)); } From 6551c682edc403bdb27fa31fa5bd519eb6579404 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 15:38:31 -0800 Subject: [PATCH 1031/1066] Working on strict null checking code/windows --- src/vs/code/electron-main/windows.ts | 45 +++++++++---------- .../platform/driver/electron-main/driver.ts | 10 ++++- .../platform/windows/electron-main/windows.ts | 4 +- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 6b4a7ae736e..59bec7973b6 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -18,7 +18,7 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, OpenDialogOptions } from 'vs/platform/windows/common/windows'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import product from 'vs/platform/node/product'; @@ -50,7 +50,7 @@ interface INewWindowState extends ISingleWindowState { interface IWindowState { workspace?: IWorkspaceIdentifier; folderUri?: URI; - backupPath: string; + backupPath?: string; remoteAuthority?: string; uiState: ISingleWindowState; } @@ -131,7 +131,7 @@ export class WindowsManager implements IWindowsMainService { private initialUserEnv: IProcessEnvironment; private windowsState: IWindowsState; - private lastClosedWindowState: IWindowState; + private lastClosedWindowState?: IWindowState; private dialogs: Dialogs; private workspacesManager: WorkspacesManager; @@ -188,8 +188,9 @@ export class WindowsManager implements IWindowsMainService { if (windowState.folderUri) { windowState.folderUri = URI.revive(windowState.folderUri); } - if ((windowState).folderPath) { - windowState.folderUri = URI.file((windowState).folderPath); + const folderPath = (windowState as IBackwardCompatibleWindowState).folderPath; + if (folderPath) { + windowState.folderUri = URI.file(folderPath); } return windowState; } @@ -403,12 +404,12 @@ export class WindowsManager implements IWindowsMainService { // // These are windows to open to show workspaces // - const workspacesToOpen = arrays.distinct(pathsToOpen.filter(win => !!win.workspace).map(win => win.workspace), workspace => workspace.id); // prevent duplicates + const workspacesToOpen = arrays.distinct(arrays.coalesce(pathsToOpen.map(win => win.workspace)), workspace => workspace.id); // prevent duplicates // // These are windows to open to show either folders or files (including diffing files or creating them) // - const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderUri && !win.fileUri).map(win => win.folderUri), folder => getComparisonKey(folder)); // prevent duplicates + const foldersToOpen = arrays.distinct(arrays.coalesce(pathsToOpen.filter(win => win.folderUri && !win.fileUri).map(win => win.folderUri)), folder => getComparisonKey(folder)); // prevent duplicates // // These are windows to restore because of hot-exit or from previous session (only performed once on startup!) @@ -423,7 +424,7 @@ export class WindowsManager implements IWindowsMainService { workspacesToRestore.push(...this.workspacesMainService.getUntitledWorkspacesSync()); // collect from previous window session emptyToRestore = this.backupMainService.getEmptyWindowBackupPaths(); - emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath), remoteAuthority: w.remoteAuthority }))); // add empty windows with backupPath + emptyToRestore.push(...pathsToOpen.filter(w => !w.workspace && !w.folderUri && w.backupPath).map(w => ({ backupFolder: basename(w.backupPath!), remoteAuthority: w.remoteAuthority }))); // add empty windows with backupPath emptyToRestore = arrays.distinct(emptyToRestore, info => info.backupFolder); // prevent duplicates } @@ -444,9 +445,9 @@ export class WindowsManager implements IWindowsMainService { // 1.) focus last active window if we are not instructed to open any paths if (focusLastActive) { - const lastActiveWindw = usedWindows.filter(w => w.backupPath === this.windowsState.lastActiveWindow.backupPath); - if (lastActiveWindw.length) { - lastActiveWindw[0].focus(); + const lastActiveWindow = usedWindows.filter(w => w.backupPath === this.windowsState.lastActiveWindow!.backupPath); + if (lastActiveWindow.length) { + lastActiveWindow[0].focus(); focusLastOpened = false; focusLastWindow = false; } @@ -1593,7 +1594,7 @@ export class WindowsManager implements IWindowsMainService { }); } - getFocusedWindow(): ICodeWindow { + getFocusedWindow(): ICodeWindow | null { const win = BrowserWindow.getFocusedWindow(); if (win) { return this.getWindowById(win.id); @@ -1602,7 +1603,7 @@ export class WindowsManager implements IWindowsMainService { return null; } - getWindowById(windowId: number): ICodeWindow { + getWindowById(windowId: number): ICodeWindow | null { const res = WindowsManager.WINDOWS.filter(w => w.id === windowId); if (res && res.length === 1) { return res[0]; @@ -1719,9 +1720,8 @@ export class WindowsManager implements IWindowsMainService { internalOptions.pickFolders = pickFolders; internalOptions.pickFiles = pickFiles; - if (!internalOptions.dialogOptions) { - internalOptions.dialogOptions = Object.create(null); - } + const dialogOptions: OpenDialogOptions = internalOptions.dialogOptions || Object.create(null); + internalOptions.dialogOptions = dialogOptions; if (!internalOptions.dialogOptions.title) { if (pickFolders && pickFiles) { @@ -1952,13 +1952,12 @@ class Dialogs { class WorkspacesManager { constructor( - private workspacesMainService: IWorkspacesMainService, - private backupMainService: IBackupMainService, - private environmentService: IEnvironmentService, - private historyMainService: IHistoryMainService, - private windowsMainService: IWindowsMainService, - ) { - } + private readonly workspacesMainService: IWorkspacesMainService, + private readonly backupMainService: IBackupMainService, + private readonly environmentService: IEnvironmentService, + private readonly historyMainService: IHistoryMainService, + private readonly windowsMainService: IWindowsMainService, + ) { } enterWorkspace(window: ICodeWindow, path: URI): Promise { if (!window || !window.win || !window.isReady) { diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index cc6eecc1819..deab8063bb3 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -57,9 +57,11 @@ export class Driver implements IDriver, IWindowDriverRegistry { await this.whenUnfrozen(windowId); const window = this.windowsService.getWindowById(windowId); + if (!window) { + throw new Error('Invalid window'); + } const webContents = window.win.webContents; const image = await new Promise(c => webContents.capturePage(c)); - return image.toPNG().toString('base64'); } @@ -67,6 +69,9 @@ export class Driver implements IDriver, IWindowDriverRegistry { await this.whenUnfrozen(windowId); const window = this.windowsService.getWindowById(windowId); + if (!window) { + throw new Error('Invalid window'); + } this.reloadingWindowIds.add(windowId); this.windowsService.reload(window); } @@ -97,6 +102,9 @@ export class Driver implements IDriver, IWindowDriverRegistry { } const window = this.windowsService.getWindowById(windowId); + if (!window) { + throw new Error('Invalid window'); + } const webContents = window.win.webContents; const noModifiedKeybinding = new SimpleKeybinding(false, false, false, false, keybinding.keyCode); const resolvedKeybinding = new USLayoutResolvedKeybinding(noModifiedKeybinding, OS); diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 19219a64149..d8b43f4c13b 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -112,8 +112,8 @@ export interface IWindowsMainService { openNewTabbedWindow(context: OpenContext): ICodeWindow[]; sendToFocused(channel: string, ...args: any[]): void; sendToAll(channel: string, payload: any, windowIdsToIgnore?: number[]): void; - getFocusedWindow(): ICodeWindow; - getWindowById(windowId: number): ICodeWindow; + getFocusedWindow(): ICodeWindow | null; + getWindowById(windowId: number): ICodeWindow | null; getWindows(): ICodeWindow[]; getWindowCount(): number; quit(): void; From 7842634a387b9d127ba188faabd1b69219caa72a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 15:50:53 -0800 Subject: [PATCH 1032/1066] Strict null supression in some test files --- .../test/common/editor/editor.test.ts | 26 +++--- .../api/extHostApiCommands.test.ts | 80 +++++++++---------- .../api/extHostConfiguration.test.ts | 52 ++++++------ .../api/extHostLanguageFeatures.test.ts | 4 +- 4 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/vs/workbench/test/common/editor/editor.test.ts b/src/vs/workbench/test/common/editor/editor.test.ts index 1e88e3763c0..35cf8cc38b9 100644 --- a/src/vs/workbench/test/common/editor/editor.test.ts +++ b/src/vs/workbench/test/common/editor/editor.test.ts @@ -32,7 +32,7 @@ class FileEditorInput extends EditorInput { return this.resource; } - resolve(): Promise { + resolve(): Promise { return Promise.resolve(null); } } @@ -55,22 +55,22 @@ suite('Workbench editor', () => { test('toResource', () => { const service = accessor.untitledEditorService; - assert.ok(!toResource(null)); + assert.ok(!toResource(null!)); const untitled = service.createOrGet(); - assert.equal(toResource(untitled).toString(), untitled.getResource().toString()); - assert.equal(toResource(untitled, { supportSideBySide: true }).toString(), untitled.getResource().toString()); - assert.equal(toResource(untitled, { filter: Schemas.untitled }).toString(), untitled.getResource().toString()); - assert.equal(toResource(untitled, { filter: [Schemas.file, Schemas.untitled] }).toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled)!.toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { supportSideBySide: true })!.toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { filter: Schemas.untitled })!.toString(), untitled.getResource().toString()); + assert.equal(toResource(untitled, { filter: [Schemas.file, Schemas.untitled] })!.toString(), untitled.getResource().toString()); assert.ok(!toResource(untitled, { filter: Schemas.file })); const file = new FileEditorInput(URI.file('/some/path.txt')); - assert.equal(toResource(file).toString(), file.getResource().toString()); - assert.equal(toResource(file, { supportSideBySide: true }).toString(), file.getResource().toString()); - assert.equal(toResource(file, { filter: Schemas.file }).toString(), file.getResource().toString()); - assert.equal(toResource(file, { filter: [Schemas.file, Schemas.untitled] }).toString(), file.getResource().toString()); + assert.equal(toResource(file)!.toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true })!.toString(), file.getResource().toString()); + assert.equal(toResource(file, { filter: Schemas.file })!.toString(), file.getResource().toString()); + assert.equal(toResource(file, { filter: [Schemas.file, Schemas.untitled] })!.toString(), file.getResource().toString()); assert.ok(!toResource(file, { filter: Schemas.untitled })); const diffEditorInput = new DiffEditorInput('name', 'description', untitled, file); @@ -79,8 +79,8 @@ suite('Workbench editor', () => { assert.ok(!toResource(diffEditorInput, { filter: Schemas.file })); assert.ok(!toResource(diffEditorInput, { supportSideBySide: false })); - assert.equal(toResource(file, { supportSideBySide: true }).toString(), file.getResource().toString()); - assert.equal(toResource(file, { supportSideBySide: true, filter: Schemas.file }).toString(), file.getResource().toString()); - assert.equal(toResource(file, { supportSideBySide: true, filter: [Schemas.file, Schemas.untitled] }).toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true })!.toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true, filter: Schemas.file })!.toString(), file.getResource().toString()); + assert.equal(toResource(file, { supportSideBySide: true, filter: [Schemas.file, Schemas.untitled] })!.toString(), file.getResource().toString()); }); }); \ No newline at end of file diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 83cbe12ea65..3d51de87db9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -423,32 +423,32 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.equal(values.length, 4); let [first, second, third, fourth] = values; assert.equal(first.label, 'item1'); - assert.equal(first.textEdit.newText, 'item1'); - assert.equal(first.textEdit.range.start.line, 0); - assert.equal(first.textEdit.range.start.character, 0); - assert.equal(first.textEdit.range.end.line, 0); - assert.equal(first.textEdit.range.end.character, 4); + assert.equal(first.textEdit!.newText, 'item1'); + assert.equal(first.textEdit!.range.start.line, 0); + assert.equal(first.textEdit!.range.start.character, 0); + assert.equal(first.textEdit!.range.end.line, 0); + assert.equal(first.textEdit!.range.end.character, 4); assert.equal(second.label, 'item2'); - assert.equal(second.textEdit.newText, 'foo'); - assert.equal(second.textEdit.range.start.line, 0); - assert.equal(second.textEdit.range.start.character, 4); - assert.equal(second.textEdit.range.end.line, 0); - assert.equal(second.textEdit.range.end.character, 8); + assert.equal(second.textEdit!.newText, 'foo'); + assert.equal(second.textEdit!.range.start.line, 0); + assert.equal(second.textEdit!.range.start.character, 4); + assert.equal(second.textEdit!.range.end.line, 0); + assert.equal(second.textEdit!.range.end.character, 8); assert.equal(third.label, 'item3'); - assert.equal(third.textEdit.newText, 'foobar'); - assert.equal(third.textEdit.range.start.line, 0); - assert.equal(third.textEdit.range.start.character, 1); - assert.equal(third.textEdit.range.end.line, 0); - assert.equal(third.textEdit.range.end.character, 6); + assert.equal(third.textEdit!.newText, 'foobar'); + assert.equal(third.textEdit!.range.start.line, 0); + assert.equal(third.textEdit!.range.start.character, 1); + assert.equal(third.textEdit!.range.end.line, 0); + assert.equal(third.textEdit!.range.end.character, 6); assert.equal(fourth.label, 'item4'); assert.equal(fourth.textEdit, undefined); - assert.equal(fourth.range.start.line, 0); - assert.equal(fourth.range.start.character, 1); - assert.equal(fourth.range.end.line, 0); - assert.equal(fourth.range.end.character, 4); + assert.equal(fourth.range!.start.line, 0); + assert.equal(fourth.range!.start.character, 1); + assert.equal(fourth.range!.end.line, 0); + assert.equal(fourth.range!.end.character, 4); assert.ok(fourth.insertText instanceof types.SnippetString); assert.equal((fourth.insertText).value, 'foo$0bar'); }); @@ -631,11 +631,11 @@ suite('ExtHostLanguageFeatureCommands', function () { return rpcProtocol.sync().then(() => { return commands.executeCommand('vscode.executeCodeActionProvider', model.uri, new types.Range(0, 0, 1, 1)).then(value => { assert.equal(value.length, 1); - let [first] = value; + const [first] = value; assert.ok(first.command); - assert.equal(first.command.command, 'command'); - assert.equal(first.command.title, 'command_title'); - assert.equal(first.kind.value, 'foo'); + assert.equal(first.command!.command, 'command'); + assert.equal(first.command!.title, 'command_title'); + assert.equal(first.kind!.value, 'foo'); assert.equal(first.title, 'title'); }); @@ -661,13 +661,13 @@ suite('ExtHostLanguageFeatureCommands', function () { return rpcProtocol.sync().then(() => { return commands.executeCommand('vscode.executeCodeLensProvider', model.uri).then(value => { assert.equal(value.length, 1); - let [first] = value; + const [first] = value; - assert.equal(first.command.title, 'Title'); - assert.equal(first.command.command, 'cmd'); - assert.equal(first.command.arguments[0], 1); - assert.equal(first.command.arguments[1], true); - assert.equal(first.command.arguments[2], complexArg); + assert.equal(first.command!.title, 'Title'); + assert.equal(first.command!.command, 'cmd'); + assert.equal(first.command!.arguments[0], 1); + assert.equal(first.command!.arguments[1], true); + assert.equal(first.command!.arguments[2], complexArg); }); }); }); @@ -719,7 +719,7 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.equal(value.length, 1); let [first] = value; - assert.equal(first.target.toString(), 'foo:bar'); + assert.equal(first.target + '', 'foo:bar'); assert.equal(first.range.start.line, 0); assert.equal(first.range.start.character, 0); assert.equal(first.range.end.line, 0); @@ -765,16 +765,16 @@ suite('ExtHostLanguageFeatureCommands', function () { let [first] = value; assert.equal(first.label, '#ABC'); - assert.equal(first.textEdit.newText, '#ABC'); - assert.equal(first.textEdit.range.start.line, 1); - assert.equal(first.textEdit.range.start.character, 0); - assert.equal(first.textEdit.range.end.line, 1); - assert.equal(first.textEdit.range.end.character, 20); - assert.equal(first.additionalTextEdits.length, 1); - assert.equal(first.additionalTextEdits[0].range.start.line, 2); - assert.equal(first.additionalTextEdits[0].range.start.character, 20); - assert.equal(first.additionalTextEdits[0].range.end.line, 2); - assert.equal(first.additionalTextEdits[0].range.end.character, 20); + assert.equal(first.textEdit!.newText, '#ABC'); + assert.equal(first.textEdit!.range.start.line, 1); + assert.equal(first.textEdit!.range.start.character, 0); + assert.equal(first.textEdit!.range.end.line, 1); + assert.equal(first.textEdit!.range.end.character, 20); + assert.equal(first.additionalTextEdits!.length, 1); + assert.equal(first.additionalTextEdits![0].range.start.line, 2); + assert.equal(first.additionalTextEdits![0].range.start.character, 20); + assert.equal(first.additionalTextEdits![0].range.end.line, 2); + assert.equal(first.additionalTextEdits![0].range.end.character, 20); }); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts index 995f762f4b4..75ebe01221d 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostConfiguration.test.ts @@ -111,38 +111,38 @@ suite('ExtHostConfiguration', function () { }); let testObject = all.getConfiguration(); - let actual = testObject.get('farboo'); + let actual = testObject.get('farboo')!; actual['nested']['config1'] = 41; assert.equal(41, actual['nested']['config1']); actual['farboo1'] = 'newValue'; assert.equal('newValue', actual['farboo1']); testObject = all.getConfiguration(); - actual = testObject.get('farboo'); + actual = testObject.get('farboo')!; assert.equal(actual['nested']['config1'], 42); assert.equal(actual['farboo1'], undefined); testObject = all.getConfiguration(); - actual = testObject.get('farboo'); + actual = testObject.get('farboo')!; assert.equal(actual['config0'], true); actual['config0'] = false; assert.equal(actual['config0'], false); testObject = all.getConfiguration(); - actual = testObject.get('farboo'); + actual = testObject.get('farboo')!; assert.equal(actual['config0'], true); testObject = all.getConfiguration(); - actual = testObject.inspect('farboo'); + actual = testObject.inspect('farboo')!; actual['value'] = 'effectiveValue'; assert.equal('effectiveValue', actual['value']); testObject = all.getConfiguration('workbench'); - actual = testObject.get('colorCustomizations'); + actual = testObject.get('colorCustomizations')!; delete actual['statusBar.foreground']; assert.equal(actual['statusBar.foreground'], undefined); testObject = all.getConfiguration('workbench'); - actual = testObject.get('colorCustomizations'); + actual = testObject.get('colorCustomizations')!; assert.equal(actual['statusBar.foreground'], 'somevalue'); }); @@ -166,7 +166,7 @@ suite('ExtHostConfiguration', function () { } }); - let testObject = all.getConfiguration(); + const testObject = all.getConfiguration(); let actual = testObject.get('farboo'); assert.deepEqual(JSON.stringify({ 'config0': true, @@ -179,7 +179,7 @@ suite('ExtHostConfiguration', function () { assert.deepEqual(undefined, JSON.stringify(testObject.get('unknownkey'))); - actual = testObject.get('farboo'); + actual = testObject.get('farboo')!; actual['config0'] = false; assert.deepEqual(JSON.stringify({ 'config0': false, @@ -190,7 +190,7 @@ suite('ExtHostConfiguration', function () { 'config4': '' }), JSON.stringify(actual)); - actual = testObject.get('workbench')['colorCustomizations']; + actual = testObject.get('workbench')['colorCustomizations']!; actual['statusBar.background'] = 'anothervalue'; assert.deepEqual(JSON.stringify({ 'statusBar.foreground': 'somevalue', @@ -284,13 +284,13 @@ suite('ExtHostConfiguration', function () { } ); - let actual = testObject.getConfiguration().inspect('editor.wordWrap'); + let actual = testObject.getConfiguration().inspect('editor.wordWrap')!; assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); assert.equal(actual.workspaceFolderValue, undefined); - actual = testObject.getConfiguration('editor').inspect('wordWrap'); + actual = testObject.getConfiguration('editor').inspect('wordWrap')!; assert.equal(actual.defaultValue, 'off'); assert.equal(actual.globalValue, 'on'); assert.equal(actual.workspaceValue, undefined); @@ -331,25 +331,25 @@ suite('ExtHostConfiguration', function () { } ); - let actual1 = testObject.getConfiguration().inspect('editor.wordWrap'); + let actual1 = testObject.getConfiguration().inspect('editor.wordWrap')!; assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); assert.equal(actual1.workspaceFolderValue, undefined); - actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); + actual1 = testObject.getConfiguration('editor').inspect('wordWrap')!; assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); assert.equal(actual1.workspaceFolderValue, undefined); - let actual2 = testObject.getConfiguration(null, workspaceUri).inspect('editor.wordWrap'); + let actual2 = testObject.getConfiguration(undefined, workspaceUri).inspect('editor.wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.workspaceFolderValue, 'bounded'); - actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', workspaceUri).inspect('wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); @@ -406,62 +406,62 @@ suite('ExtHostConfiguration', function () { } ); - let actual1 = testObject.getConfiguration().inspect('editor.wordWrap'); + let actual1 = testObject.getConfiguration().inspect('editor.wordWrap')!; assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); assert.equal(actual1.workspaceFolderValue, undefined); - actual1 = testObject.getConfiguration('editor').inspect('wordWrap'); + actual1 = testObject.getConfiguration('editor').inspect('wordWrap')!; assert.equal(actual1.defaultValue, 'off'); assert.equal(actual1.globalValue, 'on'); assert.equal(actual1.workspaceValue, 'bounded'); assert.equal(actual1.workspaceFolderValue, undefined); - actual1 = testObject.getConfiguration('editor').inspect('lineNumbers'); + actual1 = testObject.getConfiguration('editor').inspect('lineNumbers')!; assert.equal(actual1.defaultValue, 'on'); assert.equal(actual1.globalValue, undefined); assert.equal(actual1.workspaceValue, undefined); assert.equal(actual1.workspaceFolderValue, undefined); - let actual2 = testObject.getConfiguration(null, firstRoot).inspect('editor.wordWrap'); + let actual2 = testObject.getConfiguration(undefined, firstRoot).inspect('editor.wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.workspaceFolderValue, 'off'); - actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.workspaceFolderValue, 'off'); - actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers'); + actual2 = testObject.getConfiguration('editor', firstRoot).inspect('lineNumbers')!; assert.equal(actual2.defaultValue, 'on'); assert.equal(actual2.globalValue, undefined); assert.equal(actual2.workspaceValue, undefined); assert.equal(actual2.workspaceFolderValue, 'relative'); - actual2 = testObject.getConfiguration(null, secondRoot).inspect('editor.wordWrap'); + actual2 = testObject.getConfiguration(undefined, secondRoot).inspect('editor.wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.workspaceFolderValue, 'on'); - actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', secondRoot).inspect('wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.equal(actual2.workspaceFolderValue, 'on'); - actual2 = testObject.getConfiguration(null, thirdRoot).inspect('editor.wordWrap'); + actual2 = testObject.getConfiguration(undefined, thirdRoot).inspect('editor.wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); assert.ok(Object.keys(actual2).indexOf('workspaceFolderValue') !== -1); assert.equal(actual2.workspaceFolderValue, undefined); - actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap'); + actual2 = testObject.getConfiguration('editor', thirdRoot).inspect('wordWrap')!; assert.equal(actual2.defaultValue, 'off'); assert.equal(actual2.globalValue, 'on'); assert.equal(actual2.workspaceValue, 'bounded'); diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 18053e12bf7..225cc4abbb5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -499,9 +499,9 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None); + const value = (await getOccurrencesAtPosition(model, new EditorPosition(1, 2), CancellationToken.None))!; assert.equal(value.length, 1); - let [entry] = value; + const [entry] = value; assert.deepEqual(entry.range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 3 }); assert.equal(entry.kind, modes.DocumentHighlightKind.Text); }); From ffe09d3642b36f5dae20450c9c6d671484f36b6a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 15:52:26 -0800 Subject: [PATCH 1033/1066] Strict null work for webview --- .../electron-browser/html.contribution.ts | 4 ++-- .../html/electron-browser/htmlPreviewPart.ts | 17 ++++++++-------- .../webview/electron-browser/webviewEditor.ts | 20 ++++++++++--------- .../electron-browser/webviewEditorInput.ts | 9 ++++++--- .../electron-browser/webviewElement.ts | 4 ++-- .../electron-browser/webviewFindWidget.ts | 14 ++++++++++--- 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/parts/html/electron-browser/html.contribution.ts b/src/vs/workbench/parts/html/electron-browser/html.contribution.ts index 73eea401555..4e4c738e8c0 100644 --- a/src/vs/workbench/parts/html/electron-browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/electron-browser/html.contribution.ts @@ -23,7 +23,7 @@ function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI return accessor.get(IEditorService).visibleControls .filter(c => c instanceof HtmlPreviewPart && c.model) .map(e => e as HtmlPreviewPart) - .filter(e => e.model.uri.scheme === uri.scheme && e.model.uri.toString() === uri.toString()); + .filter(e => e.model!.uri.scheme === uri.scheme && e.model!.uri.toString() === uri.toString()); } // --- Register Editor @@ -45,7 +45,7 @@ CommandsRegistry.registerCommand('_workbench.previewHtml', function ( const uri = resource instanceof URI ? resource : URI.parse(resource); label = label || uri.fsPath; - let input: HtmlInput; + let input: HtmlInput | undefined; const editorGroupService = accessor.get(IEditorGroupsService); diff --git a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts index 1c33d07ae78..bc5959209a4 100644 --- a/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts +++ b/src/vs/workbench/parts/html/electron-browser/htmlPreviewPart.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ITextModel } from 'vs/editor/common/model'; import { Disposable, IDisposable, dispose, IReference } from 'vs/base/common/lifecycle'; import { EditorOptions, EditorInput, IEditorMemento } from 'vs/workbench/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -38,8 +37,8 @@ export class HtmlPreviewPart extends BaseWebviewEditor { private _webviewDisposables: IDisposable[]; - private _modelRef: IReference; - public get model(): ITextModel { return this._modelRef && this._modelRef.object.textEditorModel; } + private _modelRef?: IReference; + public get model() { return this._modelRef ? this._modelRef.object.textEditorModel : undefined; } private _modelChangeSubscription = Disposable.None; private _themeChangeSubscription = Disposable.None; @@ -138,14 +137,14 @@ export class HtmlPreviewPart extends BaseWebviewEditor { this._themeChangeSubscription = this.themeService.onThemeChange(this.onThemeChange.bind(this)); if (this._hasValidModel()) { - this._modelChangeSubscription = this.model.onDidChangeContent(() => this.webview.contents = this.model.getLinesContent().join('\n')); - this.webview.contents = this.model.getLinesContent().join('\n'); + this._modelChangeSubscription = this.model!.onDidChangeContent(() => this.webview.contents = this.model!.getLinesContent().join('\n')); + this.webview.contents = this.model!.getLinesContent().join('\n'); } } } private _hasValidModel(): boolean { - return this._modelRef && this.model && !this.model.isDisposed(); + return !!(this._modelRef && this.model && !this.model.isDisposed()); } public layout(dimension: Dimension): void { @@ -248,10 +247,10 @@ export class HtmlPreviewPart extends BaseWebviewEditor { } private saveHTMLPreviewViewState(input: HtmlInput, editorViewState: HtmlPreviewEditorViewState): void { - this.editorMemento.saveEditorState(this.group, input, editorViewState); + this.editorMemento.saveEditorState(this.group!, input, editorViewState); } - private loadHTMLPreviewViewState(input: HtmlInput): HtmlPreviewEditorViewState { - return this.editorMemento.loadEditorState(this.group, input); + private loadHTMLPreviewViewState(input: HtmlInput): HtmlPreviewEditorViewState | undefined { + return this.editorMemento.loadEditorState(this.group!, input); } } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index 40896f71e7f..f5b40e19e0d 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts @@ -28,7 +28,7 @@ export class WebviewEditor extends BaseWebviewEditor { public static readonly ID = 'WebviewEditor'; private _editorFrame: HTMLElement; - private _content: HTMLElement; + private _content?: HTMLElement; private _webviewContent: HTMLElement | undefined; private _webviewFocusTrackerDisposables: IDisposable[] = []; @@ -171,8 +171,9 @@ export class WebviewEditor extends BaseWebviewEditor { if (token.isCancellationRequested) { return; } - - input.updateGroup(this.group.id); + if (this.group) { + input.updateGroup(this.group.id); + } this.updateWebview(input); }); } @@ -187,7 +188,7 @@ export class WebviewEditor extends BaseWebviewEditor { useSameOriginForRoot: false, localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots(), extensionLocation: input.extensionLocation - }, input.options.retainContextWhenHidden); + }, !!input.options.retainContextWhenHidden); if (this._webviewContent) { this._webviewContent.style.visibility = 'visible'; @@ -198,8 +199,9 @@ export class WebviewEditor extends BaseWebviewEditor { private getDefaultLocalResourceRoots(): URI[] { const rootPaths = this._contextService.getWorkspace().folders.map(x => x.uri); - if ((this.input as WebviewEditorInput).extensionLocation) { - rootPaths.push((this.input as WebviewEditorInput).extensionLocation); + const extensionLocation = (this.input as WebviewEditorInput).extensionLocation; + if (extensionLocation) { + rootPaths.push(extensionLocation); } return rootPaths; } @@ -235,7 +237,7 @@ export class WebviewEditor extends BaseWebviewEditor { this._webview.state = input.webviewState; - this._content.setAttribute('aria-flowto', this._webviewContent.id); + this._content!.setAttribute('aria-flowto', this._webviewContent.id); this.doUpdateContainer(); } @@ -254,11 +256,11 @@ export class WebviewEditor extends BaseWebviewEditor { this._webviewFocusTrackerDisposables = dispose(this._webviewFocusTrackerDisposables); // Track focus in webview content - const webviewContentFocusTracker = DOM.trackFocus(this._webviewContent); + const webviewContentFocusTracker = DOM.trackFocus(this._webviewContent!); this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker); this._webviewFocusTrackerDisposables.push(webviewContentFocusTracker.onDidFocus(() => this._onDidFocusWebview.fire())); // Track focus in webview element - this._webviewFocusTrackerDisposables.push(this._webview.onDidFocus(() => this._onDidFocusWebview.fire())); + this._webviewFocusTrackerDisposables.push(this._webview!.onDidFocus(() => this._onDidFocusWebview.fire())); } } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts index 4da45eee721..5039c05d598 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts @@ -56,7 +56,7 @@ export class WebviewEditorInput extends EditorInput { private _html: string = ''; private _currentWebviewHtml: string = ''; public _events: WebviewEvents | undefined; - private _container: HTMLElement; + private _container?: HTMLElement; private _webview: WebviewElement | undefined; private _webviewOwner: any; private _webviewDisposables: IDisposable[] = []; @@ -139,7 +139,7 @@ export class WebviewEditorInput extends EditorInput { return this.getName(); } - public getDescription(): string { + public getDescription() { return null; } @@ -240,10 +240,13 @@ export class WebviewEditorInput extends EditorInput { return this._webview; } - public set webview(value: WebviewElement) { + public set webview(value: WebviewElement | undefined) { this._webviewDisposables = dispose(this._webviewDisposables); this._webview = value; + if (!this._webview) { + return; + } this._webview.onDidClickLink(link => { if (this._events && this._events.onDidClickLink) { diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index d65d118c5bb..04aa723faa8 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -44,7 +44,7 @@ interface IKeydownEvent { class WebviewProtocolProvider extends Disposable { constructor( webview: Electron.WebviewTag, - private readonly _extensionLocation: URI, + private readonly _extensionLocation: URI | undefined, private readonly _getLocalResourceRoots: () => ReadonlyArray, private readonly _environmentService: IEnvironmentService, private readonly _fileService: IFileService, @@ -356,7 +356,7 @@ export class WebviewElement extends Disposable { } public mountTo(parent: HTMLElement) { - parent.appendChild(this._webviewFindWidget.getDomNode()); + parent.appendChild(this._webviewFindWidget.getDomNode()!); parent.appendChild(this._webview); } diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts b/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts index 3c881b1ba09..deffccbdfa6 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewFindWidget.ts @@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; export class WebviewFindWidget extends SimpleFindWidget { constructor( - private _webview: WebviewElement, + private _webview: WebviewElement | undefined, @IContextViewService contextViewService: IContextViewService, @IContextKeyService contextKeyService: IContextKeyService ) { @@ -24,6 +24,9 @@ export class WebviewFindWidget extends SimpleFindWidget { } public find(previous: boolean) { + if (!this._webview) { + return; + } const val = this.inputValue; if (val) { this._webview.find(val, { findNext: true, forward: !previous }); @@ -32,11 +35,16 @@ export class WebviewFindWidget extends SimpleFindWidget { public hide() { super.hide(); - this._webview.stopFind(true); - this._webview.focus(); + if (this._webview) { + this._webview.stopFind(true); + this._webview.focus(); + } } public onInputChanged() { + if (!this._webview) { + return; + } const val = this.inputValue; if (val) { this._webview.startFind(val); From 197a916fcb7a1af805c6d5bab697e259d35a7878 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 15:58:47 -0800 Subject: [PATCH 1034/1066] Fixing some strict null errors in mainThread --- .../workbench/api/electron-browser/mainThreadClipboard.ts | 2 +- .../workbench/api/electron-browser/mainThreadCommands.ts | 7 ++++--- .../workbench/api/electron-browser/mainThreadFileSystem.ts | 6 +++++- .../electron-browser/mainThreadFileSystemEventService.ts | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts index 6497256226e..80c0f71bf9e 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadClipboard.ts @@ -20,6 +20,6 @@ export class MainThreadCommands implements MainThreadClipboardShape { $writeText(value: string): Promise { clipboard.writeText(value); - return undefined; + return Promise.resolve(); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts index ebfc4171ae3..061034039c1 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadCommands.ts @@ -64,13 +64,14 @@ export class MainThreadCommands implements MainThreadCommandsShape { } $unregisterCommand(id: string): void { - if (this._disposables.has(id)) { - this._disposables.get(id).dispose(); + const command = this._disposables.get(id); + if (command) { + command.dispose(); this._disposables.delete(id); } } - $executeCommand(id: string, args: any[]): Promise { + $executeCommand(id: string, args: any[]): Promise { for (let i = 0; i < args.length; i++) { args[i] = revive(args[i], 0); } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts index caea6c69d7f..df3fac179a9 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystem.ts @@ -44,7 +44,11 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape { } $onFileSystemChange(handle: number, changes: IFileChangeDto[]): void { - this._fileProvider.get(handle).$onFileSystemChange(changes); + const fileProvider = this._fileProvider.get(handle); + if (!fileProvider) { + throw new Error('Unknown file provider'); + } + fileProvider.$onFileSystemChange(changes); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts index 2a492bf0f7a..08e4c2fd7a0 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadFileSystemEventService.ts @@ -52,7 +52,7 @@ export class MainThreadFileSystemEventService { // file operation events - (changes the editor makes) fileService.onAfterOperation(e => { if (e.operation === FileOperation.MOVE) { - proxy.$onFileRename(e.resource, e.target.resource); + proxy.$onFileRename(e.resource, e.target.resource!); } }, undefined, this._listener); From b6087b202131fccbb431229932d7a0cdfc5d05a3 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 15:59:38 -0800 Subject: [PATCH 1035/1066] Fixing more strict null errros in extHost and related files --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 10 +++++----- src/vs/workbench/api/node/apiCommands.ts | 6 +++--- src/vs/workbench/api/node/extHost.protocol.ts | 2 +- src/vs/workbench/api/node/extHostConfiguration.ts | 12 ++++++------ .../api/node/extHostFileSystemEventService.ts | 2 +- src/vs/workbench/api/node/extHostMessageService.ts | 2 +- src/vs/workbench/api/node/extHostStorage.ts | 2 +- src/vs/workbench/api/node/extHostWebview.ts | 2 +- .../workbench/common/editor/textDiffEditorModel.ts | 4 ++-- src/vs/workbench/electron-browser/main.ts | 6 +++--- .../test/electron-browser/api/testRPCProtocol.ts | 8 ++++---- 11 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index fbffb5d002f..4674ec350c6 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -358,11 +358,11 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return; } - let container = this.getViewContainer(entry.key); - if (!container) { + const viewContainer = this.getViewContainer(entry.key); + if (!viewContainer) { collector.warn(localize('ViewContainerDoesnotExist', "View container '{0}' does not exist and all views registered to it will be added to 'Explorer'.", entry.key)); - container = this.getDefaultViewContainer(); } + const container = viewContainer || this.getDefaultViewContainer(); const registeredViews = ViewsRegistry.getViews(container); const viewIds: string[] = []; const viewDescriptors = coalesce(entry.value.map((item, index) => { @@ -398,7 +398,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } private getDefaultViewContainer(): ViewContainer { - return this.viewContainersRegistry.get(EXPLORER); + return this.viewContainersRegistry.get(EXPLORER)!; } private removeViews(extensions: IExtensionPointUser[]): void { @@ -435,7 +435,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { return true; } - private getViewContainer(value: string): ViewContainer { + private getViewContainer(value: string): ViewContainer | undefined { switch (value) { case 'explorer': return this.viewContainersRegistry.get(EXPLORER); case 'debug': return this.viewContainersRegistry.get(DEBUG); diff --git a/src/vs/workbench/api/node/apiCommands.ts b/src/vs/workbench/api/node/apiCommands.ts index 0e808dbfed4..b6691b812d1 100644 --- a/src/vs/workbench/api/node/apiCommands.ts +++ b/src/vs/workbench/api/node/apiCommands.ts @@ -27,7 +27,7 @@ import { generateUuid } from 'vs/base/common/uuid'; // ----------------------------------------------------------------- export interface ICommandsExecutor { - executeCommand(id: string, ...args: any[]): Promise; + executeCommand(id: string, ...args: any[]): Promise; } function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler { @@ -84,8 +84,8 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand export class OpenAPICommand { public static ID = 'vscode.open'; public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise { - let options: ITextEditorOptions; - let position: EditorViewColumn; + let options: ITextEditorOptions | undefined; + let position: EditorViewColumn | undefined; if (columnOrOptions) { if (typeof columnOrOptions === 'number') { diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 93bf62e21a9..a66f695c65c 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -102,7 +102,7 @@ export interface MainThreadClipboardShape extends IDisposable { export interface MainThreadCommandsShape extends IDisposable { $registerCommand(id: string): void; $unregisterCommand(id: string): void; - $executeCommand(id: string, args: any[]): Promise; + $executeCommand(id: string, args: any[]): Promise; $getCommands(): Promise; } diff --git a/src/vs/workbench/api/node/extHostConfiguration.ts b/src/vs/workbench/api/node/extHostConfiguration.ts index 3e73209ffbb..b65cdf1f2bf 100644 --- a/src/vs/workbench/api/node/extHostConfiguration.ts +++ b/src/vs/workbench/api/node/extHostConfiguration.ts @@ -43,7 +43,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { private readonly _proxy: MainThreadConfigurationShape; private readonly _extHostWorkspace: ExtHostWorkspace; private readonly _barrier: Barrier; - private _actual: ExtHostConfigProvider; + private _actual: ExtHostConfigProvider | null; constructor(proxy: MainThreadConfigurationShape, extHostWorkspace: ExtHostWorkspace) { this._proxy = proxy; @@ -53,7 +53,7 @@ export class ExtHostConfiguration implements ExtHostConfigurationShape { } public getConfigProvider(): Promise { - return this._barrier.wait().then(_ => this._actual); + return this._barrier.wait().then(_ => this._actual!); } $initializeConfiguration(data: IConfigurationInitData): void { @@ -93,14 +93,14 @@ export class ExtHostConfigProvider { getConfiguration(section?: string, resource?: URI, extensionId?: ExtensionIdentifier): vscode.WorkspaceConfiguration { const config = this._toReadonlyValue(section - ? lookUp(this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace), section) - : this._configuration.getValue(null, { resource }, this._extHostWorkspace.workspace)); + ? lookUp(this._configuration.getValue(undefined, { resource }, this._extHostWorkspace.workspace), section) + : this._configuration.getValue(undefined, { resource }, this._extHostWorkspace.workspace)); if (section) { this._validateConfigurationAccess(section, resource, extensionId); } - function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget { + function parseConfigurationTarget(arg: boolean | ExtHostConfigurationTarget): ConfigurationTarget | null { if (arg === undefined || arg === null) { return null; } @@ -218,7 +218,7 @@ export class ExtHostConfigProvider { return readonlyProxy(result); } - private _validateConfigurationAccess(key: string, resource: URI, extensionId: ExtensionIdentifier): void { + private _validateConfigurationAccess(key: string, resource: URI | undefined, extensionId: ExtensionIdentifier): void { const scope = OVERRIDE_PROPERTY_PATTERN.test(key) ? ConfigurationScope.RESOURCE : this._configurationScopes[key]; const extensionIdText = extensionId ? `[${extensionId.value}] ` : ''; if (ConfigurationScope.RESOURCE === scope) { diff --git a/src/vs/workbench/api/node/extHostFileSystemEventService.ts b/src/vs/workbench/api/node/extHostFileSystemEventService.ts index c42b36dd383..f7f682b6281 100644 --- a/src/vs/workbench/api/node/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/node/extHostFileSystemEventService.ts @@ -163,7 +163,7 @@ export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServ bucket.push(wrappedThenable); } }; - }).then(() => { + }).then((): any => { if (edits.length === 0) { return undefined; } diff --git a/src/vs/workbench/api/node/extHostMessageService.ts b/src/vs/workbench/api/node/extHostMessageService.ts index f24be5f48b0..ea85d1d6d99 100644 --- a/src/vs/workbench/api/node/extHostMessageService.ts +++ b/src/vs/workbench/api/node/extHostMessageService.ts @@ -42,7 +42,7 @@ export class ExtHostMessageService { commands.push({ title: command, handle, isCloseAffordance: false }); } else if (typeof command === 'object') { let { title, isCloseAffordance } = command; - commands.push({ title, isCloseAffordance, handle }); + commands.push({ title, isCloseAffordance: !!isCloseAffordance, handle }); } else { console.warn('Invalid message item:', command); } diff --git a/src/vs/workbench/api/node/extHostStorage.ts b/src/vs/workbench/api/node/extHostStorage.ts index 8925cdbb4d0..32df5a99877 100644 --- a/src/vs/workbench/api/node/extHostStorage.ts +++ b/src/vs/workbench/api/node/extHostStorage.ts @@ -23,7 +23,7 @@ export class ExtHostStorage implements ExtHostStorageShape { this._proxy = mainContext.getProxy(MainContext.MainThreadStorage); } - getValue(shared: boolean, key: string, defaultValue?: T): Promise { + getValue(shared: boolean, key: string, defaultValue?: T): Promise { return this._proxy.$getValue(shared, key).then(value => value || defaultValue); } diff --git a/src/vs/workbench/api/node/extHostWebview.ts b/src/vs/workbench/api/node/extHostWebview.ts index a39904bd0ff..0906dd92aad 100644 --- a/src/vs/workbench/api/node/extHostWebview.ts +++ b/src/vs/workbench/api/node/extHostWebview.ts @@ -80,7 +80,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel { private readonly _proxy: MainThreadWebviewsShape; private readonly _viewType: string; private _title: string; - private _iconPath: IconPath; + private _iconPath?: IconPath; private readonly _options: vscode.WebviewPanelOptions; private readonly _webview: ExtHostWebview; diff --git a/src/vs/workbench/common/editor/textDiffEditorModel.ts b/src/vs/workbench/common/editor/textDiffEditorModel.ts index fda5a788dd6..3b9c750f528 100644 --- a/src/vs/workbench/common/editor/textDiffEditorModel.ts +++ b/src/vs/workbench/common/editor/textDiffEditorModel.ts @@ -13,7 +13,7 @@ import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; * and the modified version. */ export class TextDiffEditorModel extends DiffEditorModel { - private _textDiffEditorModel: IDiffEditorModel; + private _textDiffEditorModel: IDiffEditorModel | null; constructor(originalModel: BaseTextEditorModel, modifiedModel: BaseTextEditorModel) { super(originalModel, modifiedModel); @@ -56,7 +56,7 @@ export class TextDiffEditorModel extends DiffEditorModel { } } - get textDiffEditorModel(): IDiffEditorModel { + get textDiffEditorModel(): IDiffEditorModel | null { return this._textDiffEditorModel; } diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 1e6333c7314..1a7ba1c6ca0 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -164,7 +164,7 @@ function createWorkspaceInitializationPayload(configuration: IWindowConfiguratio } // Single-folder workspace - let workspaceInitializationPayload: Promise = Promise.resolve(); + let workspaceInitializationPayload: Promise = Promise.resolve(); if (configuration.folderUri) { workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri); } @@ -189,7 +189,7 @@ function createWorkspaceInitializationPayload(configuration: IWindowConfiguratio }); } -function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { +function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFolderWorkspaceIdentifier): Promise { // Return early the folder is not local if (folderUri.scheme !== Schemas.file) { @@ -197,7 +197,7 @@ function resolveSingleFolderWorkspaceInitializationPayload(folderUri: ISingleFol } function computeLocalDiskFolderId(folder: uri, stat: fs.Stats): string { - let ctime: number; + let ctime: number | undefined; if (platform.isLinux) { ctime = stat.ino; // Linux: birthtime is ctime, so we cannot use it! We use the ino instead! } else if (platform.isMacintosh) { diff --git a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts index 4bd240bee45..9e5f7c57d9e 100644 --- a/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts +++ b/src/vs/workbench/test/electron-browser/api/testRPCProtocol.ts @@ -10,23 +10,23 @@ import { isThenable } from 'vs/base/common/async'; export function SingleProxyRPCProtocol(thing: any): IExtHostContext { return { - remoteAuthority: null, + remoteAuthority: null!, getProxy(): T { return thing; }, set(identifier: ProxyIdentifier, value: R): R { return value; }, - assertRegistered: undefined + assertRegistered: undefined! }; } export class TestRPCProtocol implements IExtHostContext { - public remoteAuthority = null; + public remoteAuthority = null!; private _callCountValue: number = 0; - private _idle: Promise; + private _idle?: Promise; private _completeIdle: Function; private readonly _locals: { [id: string]: any; }; From a118203d7262210f4497c82859476503ce5c6c21 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 16:04:21 -0800 Subject: [PATCH 1036/1066] Remove old html post message logic This internal command has been replaced by the webview api's `postMessage` command. --- .../electron-browser/html.contribution.ts | 20 ------------------- .../webview/electron-browser/webview-pre.js | 8 ++------ .../webview/electron-browser/webviewEditor.ts | 2 -- .../electron-browser/webviewEditorInput.ts | 1 - .../electron-browser/webviewElement.ts | 3 +-- 5 files changed, 3 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/parts/html/electron-browser/html.contribution.ts b/src/vs/workbench/parts/html/electron-browser/html.contribution.ts index 4e4c738e8c0..52d81834f51 100644 --- a/src/vs/workbench/parts/html/electron-browser/html.contribution.ts +++ b/src/vs/workbench/parts/html/electron-browser/html.contribution.ts @@ -18,14 +18,6 @@ import { IExtensionsWorkbenchService } from 'vs/workbench/parts/extensions/commo import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { registerWebViewCommands } from 'vs/workbench/parts/webview/electron-browser/webview.contribution'; -function getActivePreviewsForResource(accessor: ServicesAccessor, resource: URI | string) { - const uri = resource instanceof URI ? resource : URI.parse(resource); - return accessor.get(IEditorService).visibleControls - .filter(c => c instanceof HtmlPreviewPart && c.model) - .map(e => e as HtmlPreviewPart) - .filter(e => e.model!.uri.scheme === uri.scheme && e.model!.uri.toString() === uri.toString()); -} - // --- Register Editor (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( @@ -86,16 +78,4 @@ CommandsRegistry.registerCommand('_workbench.previewHtml', function ( .then(editor => true); }); -CommandsRegistry.registerCommand('_workbench.htmlPreview.postMessage', function ( - accessor: ServicesAccessor, - resource: URI | string, - message: any -) { - const activePreviews = getActivePreviewsForResource(accessor, resource); - for (const preview of activePreviews) { - preview.sendMessage(message); - } - return activePreviews.length > 0; -}); - registerWebViewCommands(HtmlPreviewPart.ID); \ No newline at end of file diff --git a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js index ebf0555f42f..19b27d86af7 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webview-pre.js +++ b/src/vs/workbench/parts/webview/electron-browser/webview-pre.js @@ -57,7 +57,6 @@ let firstLoad = true; let loadTimeout; let pendingMessages = []; - let enableWrappedPostMessage = false; let isInDevelopmentMode = false; const initData = { @@ -190,11 +189,8 @@ // update iframe-contents ipcRenderer.on('content', (_event, data) => { const options = data.options; - enableWrappedPostMessage = options && options.enableWrappedPostMessage; - if (enableWrappedPostMessage) { - registerVscodeResourceScheme(); - } + registerVscodeResourceScheme(); const text = data.contents; const newDocument = new DOMParser().parseFromString(text, 'text/html'); @@ -213,7 +209,7 @@ } // apply default script - if (enableWrappedPostMessage && options.allowScripts) { + if (options.allowScripts) { const defaultScript = newDocument.createElement('script'); defaultScript.textContent = ` const acquireVsCodeApi = (function() { diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts index f5b40e19e0d..dbf7c661978 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditor.ts @@ -184,7 +184,6 @@ export class WebviewEditor extends BaseWebviewEditor { webview.update(input.html, { allowScripts: input.options.enableScripts, allowSvgs: true, - enableWrappedPostMessage: true, useSameOriginForRoot: false, localResourceRoots: input.options.localResourceRoots || this.getDefaultLocalResourceRoots(), extensionLocation: input.extensionLocation @@ -224,7 +223,6 @@ export class WebviewEditor extends BaseWebviewEditor { this._webview = this._instantiationService.createInstance(WebviewElement, this._partService.getContainer(Parts.EDITOR_PART), { - enableWrappedPostMessage: true, useSameOriginForRoot: false, extensionLocation: input.extensionLocation }); diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts index 5039c05d598..d07c498efd5 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewEditorInput.ts @@ -208,7 +208,6 @@ export class WebviewEditorInput extends EditorInput { this._webview.options = { allowScripts: this._options.enableScripts, allowSvgs: true, - enableWrappedPostMessage: true, useSameOriginForRoot: false, localResourceRoots: this._options.localResourceRoots }; diff --git a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts index 04aa723faa8..1727708a5c2 100644 --- a/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts +++ b/src/vs/workbench/parts/webview/electron-browser/webviewElement.ts @@ -24,7 +24,6 @@ export interface WebviewOptions { readonly allowScripts?: boolean; readonly allowSvgs?: boolean; readonly svgWhiteList?: string[]; - readonly enableWrappedPostMessage?: boolean; readonly useSameOriginForRoot?: boolean; readonly localResourceRoots?: ReadonlyArray; readonly extensionLocation?: URI; @@ -304,7 +303,7 @@ export class WebviewElement extends Disposable { this._register(addDisposableListener(this._webview, 'ipc-message', (event) => { switch (event.channel) { case 'onmessage': - if (this._options.enableWrappedPostMessage && event.args && event.args.length) { + if (event.args && event.args.length) { this._onMessage.fire(event.args[0]); } return; From c39efd9a2e380fc3ea5ef8a9998a415816be7d4f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 16:10:48 -0800 Subject: [PATCH 1037/1066] Move the SignatureContext.activeSignatureHelp api out of proposed #67940 --- src/vs/vscode.d.ts | 8 ++++++++ src/vs/vscode.proposed.d.ts | 12 ------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index e68bdf1b04f..7fa1945c4b2 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3184,6 +3184,14 @@ declare module 'vscode' { * typing a trigger character, a cursor move, or document content changes. */ readonly isRetrigger: boolean; + + /** + * The currently active [`SignatureHelp`](#SignatureHelp). + * + * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on + * the user arrowing through available signatures. + */ + readonly activeSignatureHelp?: SignatureHelp; } /** diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6192f6b1bbc..7bc245848d0 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1126,18 +1126,6 @@ declare module 'vscode' { } //#endregion - //#region SignatureHelpContext active parameters - mjbvz - export interface SignatureHelpContext { - /** - * The currently active [`SignatureHelp`](#SignatureHelp). - * - * The `activeSignatureHelp` has its [`SignatureHelp.activeSignature`] field updated based on - * the user arrowing through available signatures. - */ - readonly activeSignatureHelp?: SignatureHelp; - } - //#endregion - //#region CodeAction.isPreferred - mjbvz export interface CodeAction { /** From 476d7fe0abd3c680eb642856911f86ada26aa087 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 16:56:45 -0800 Subject: [PATCH 1038/1066] Refactor markdown contributions Reducing code duplication and reducing state --- .../src/features/preview.ts | 22 +-- .../src/features/previewContentProvider.ts | 8 +- .../src/features/previewManager.ts | 4 +- .../src/markdownEngine.ts | 6 +- .../src/markdownExtensions.ts | 169 +++++++++--------- .../src/test/engine.ts | 10 +- 6 files changed, 112 insertions(+), 107 deletions(-) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index c82cee6c7d7..4c0d0911296 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -13,7 +13,7 @@ import { disposeAll } from '../util/dispose'; import * as nls from 'vscode-nls'; import { getVisibleLine, MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreviewConfigurationManager } from './previewConfig'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { isMarkdownFile } from '../util/file'; import { resolveLinkToMarkdownFile } from '../commands/openDocumentLink'; const localize = nls.loadMessageBundle(); @@ -85,7 +85,7 @@ export class MarkdownPreview { previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributions: MarkdownContributions, + contributionProvider: MarkdownContributionProvider, ): Promise { const resource = vscode.Uri.parse(state.resource); const locked = state.locked; @@ -99,9 +99,9 @@ export class MarkdownPreview { previewConfigurations, logger, topmostLineMonitor, - contributions); + contributionProvider); - preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributions); + preview.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions); if (!isNaN(line)) { preview.line = line; @@ -118,14 +118,14 @@ export class MarkdownPreview { previewConfigurations: MarkdownPreviewConfigurationManager, logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - contributions: MarkdownContributions + contributionProvider: MarkdownContributionProvider ): MarkdownPreview { const webview = vscode.window.createWebviewPanel( MarkdownPreview.viewType, MarkdownPreview.getPreviewTitle(resource, locked), previewColumn, { enableFindWidget: true, - ...MarkdownPreview.getWebviewOptions(resource, contributions) + ...MarkdownPreview.getWebviewOptions(resource, contributionProvider.contributions) }); return new MarkdownPreview( @@ -136,7 +136,7 @@ export class MarkdownPreview { previewConfigurations, logger, topmostLineMonitor, - contributions); + contributionProvider); } private constructor( @@ -147,7 +147,7 @@ export class MarkdownPreview { private readonly _previewConfigurations: MarkdownPreviewConfigurationManager, private readonly _logger: Logger, topmostLineMonitor: MarkdownFileTopmostLineMonitor, - private readonly _contributions: MarkdownContributions, + private readonly _contributionProvider: MarkdownContributionProvider, ) { this._resource = resource; this._locked = locked; @@ -328,7 +328,7 @@ export class MarkdownPreview { } private get iconPath() { - const root = path.join(this._contributions.extensionPath, 'media'); + const root = path.join(this._contributionProvider.extensionPath, 'media'); return { light: vscode.Uri.file(path.join(root, 'Preview.svg')), dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg')) @@ -392,7 +392,7 @@ export class MarkdownPreview { if (this._resource === resource) { this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked); this.editor.iconPath = this.iconPath; - this.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, this._contributions); + this.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, this._contributionProvider.contributions); this.editor.webview.html = content; } } @@ -410,7 +410,7 @@ export class MarkdownPreview { private static getLocalResourceRoots( resource: vscode.Uri, contributions: MarkdownContributions - ): vscode.Uri[] { + ): ReadonlyArray { const baseRoots = contributions.previewResourceRoots; const folder = vscode.workspace.getWorkspaceFolder(resource); diff --git a/extensions/markdown-language-features/src/features/previewContentProvider.ts b/extensions/markdown-language-features/src/features/previewContentProvider.ts index c5efa00ac3c..88e9cea04f9 100644 --- a/extensions/markdown-language-features/src/features/previewContentProvider.ts +++ b/extensions/markdown-language-features/src/features/previewContentProvider.ts @@ -13,7 +13,7 @@ const localize = nls.loadMessageBundle(); import { Logger } from '../logger'; import { ContentSecurityPolicyArbiter, MarkdownPreviewSecurityLevel } from '../security'; import { MarkdownPreviewConfigurationManager, MarkdownPreviewConfiguration } from './previewConfig'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider } from '../markdownExtensions'; /** * Strings used inside the markdown preview. @@ -40,7 +40,7 @@ export class MarkdownContentProvider { private readonly engine: MarkdownEngine, private readonly context: vscode.ExtensionContext, private readonly cspArbiter: ContentSecurityPolicyArbiter, - private readonly contributions: MarkdownContributions, + private readonly contributionProvider: MarkdownContributionProvider, private readonly logger: Logger ) { } @@ -163,7 +163,7 @@ export class MarkdownContentProvider { } private getStyles(resource: vscode.Uri, nonce: string, config: MarkdownPreviewConfiguration, state?: any): string { - const baseStyles = this.contributions.previewStyles + const baseStyles = this.contributionProvider.contributions.previewStyles .map(resource => ``) .join('\n'); @@ -174,7 +174,7 @@ export class MarkdownContentProvider { } private getScripts(nonce: string): string { - return this.contributions.previewScripts + return this.contributionProvider.contributions.previewScripts .map(resource => ``) .join('\n'); } diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts index 15e414f762e..aed62942826 100644 --- a/extensions/markdown-language-features/src/features/previewManager.ts +++ b/extensions/markdown-language-features/src/features/previewManager.ts @@ -5,7 +5,7 @@ import * as vscode from 'vscode'; import { Logger } from '../logger'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider } from '../markdownExtensions'; import { disposeAll } from '../util/dispose'; import { MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; import { MarkdownPreview, PreviewSettings } from './preview'; @@ -25,7 +25,7 @@ export class MarkdownPreviewManager implements vscode.WebviewPanelSerializer { public constructor( private readonly _contentProvider: MarkdownContentProvider, private readonly _logger: Logger, - private readonly _contributions: MarkdownContributions + private readonly _contributions: MarkdownContributionProvider ) { this._disposables.push(vscode.window.registerWebviewPanelSerializer(MarkdownPreview.viewType, this)); } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 25ac8f33b09..090c8ab0da4 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -7,7 +7,7 @@ import * as crypto from 'crypto'; import { MarkdownIt, Token } from 'markdown-it'; import * as path from 'path'; import * as vscode from 'vscode'; -import { MarkdownContributions } from './markdownExtensions'; +import { MarkdownContributionProvider as MarkdownContributionProvider } from './markdownExtensions'; import { Slugifier } from './slugify'; import { SkinnyTextDocument } from './tableOfContentsProvider'; import { getUriForLinkWithKnownExternalScheme } from './util/links'; @@ -57,7 +57,7 @@ export class MarkdownEngine { private _tokenCache = new TokenCache(); public constructor( - private readonly extensionPreviewResourceProvider: MarkdownContributions, + private readonly contributionProvider: MarkdownContributionProvider, private readonly slugifier: Slugifier, ) { } @@ -66,7 +66,7 @@ export class MarkdownEngine { this.md = import('markdown-it').then(async markdownIt => { let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); - for (const plugin of this.extensionPreviewResourceProvider.markdownItPlugins) { + for (const plugin of this.contributionProvider.contributions.markdownItPlugins) { try { md = (await plugin)(md); } catch { diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index 55c8d76e7a4..4ca972f5c70 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -26,96 +26,105 @@ const resolveExtensionResources = (extension: vscode.Extension, resourcePat }; export interface MarkdownContributions { - readonly extensionPath: string; - readonly previewScripts: vscode.Uri[]; - readonly previewStyles: vscode.Uri[]; - readonly markdownItPlugins: Thenable<(md: any) => any>[]; - readonly previewResourceRoots: vscode.Uri[]; + readonly previewScripts: ReadonlyArray; + readonly previewStyles: ReadonlyArray; + readonly previewResourceRoots: ReadonlyArray; + readonly markdownItPlugins: ReadonlyArray any>>; } -class MarkdownExtensionContributions implements MarkdownContributions { - private readonly _scripts: vscode.Uri[] = []; - private readonly _styles: vscode.Uri[] = []; - private readonly _previewResourceRoots: vscode.Uri[] = []; - private readonly _plugins: Thenable<(md: any) => any>[] = []; +export namespace MarkdownContributions { + export const Empty: MarkdownContributions = { + previewScripts: [], + previewStyles: [], + previewResourceRoots: [], + markdownItPlugins: [] + }; - private _loaded = false; + export function merge(a: MarkdownContributions, b: MarkdownContributions): MarkdownContributions { + return { + previewScripts: [...a.previewScripts, ...b.previewScripts], + previewStyles: [...a.previewStyles, ...b.previewStyles], + previewResourceRoots: [...a.previewResourceRoots, ...b.previewResourceRoots], + markdownItPlugins: [...a.markdownItPlugins, ...b.markdownItPlugins], + }; + } + + export function fromExtension( + extension: vscode.Extension + ): MarkdownContributions { + const contributes = extension.packageJSON && extension.packageJSON.contributes; + if (!contributes) { + return MarkdownContributions.Empty; + } + + const styles = tryLoadPreviewStyles(contributes, extension); + const scripts = tryLoadPreviewScripts(contributes, extension); + const previewResourceRoots: vscode.Uri[] = []; + if (styles.length || scripts.length) { + previewResourceRoots.push(vscode.Uri.file(extension.extensionPath)); + } + + const plugins = tryLoadMarkdownItPlugins(contributes, extension); + return { + previewScripts: scripts, + previewStyles: styles, + previewResourceRoots, + markdownItPlugins: plugins ? [plugins] : [] + }; + } + + function tryLoadMarkdownItPlugins( + contributes: any, + extension: vscode.Extension + ): Thenable<(md: any) => any> | undefined { + if (contributes['markdown.markdownItPlugins']) { + return extension.activate().then(() => { + if (extension.exports && extension.exports.extendMarkdownIt) { + return (md: any) => extension.exports.extendMarkdownIt(md); + } + return (md: any) => md; + }); + } + return undefined; + } + + function tryLoadPreviewScripts( + contributes: any, + extension: vscode.Extension + ) { + return resolveExtensionResources(extension, contributes['markdown.previewScripts']); + } + + function tryLoadPreviewStyles( + contributes: any, + extension: vscode.Extension + ) { + return resolveExtensionResources(extension, contributes['markdown.previewStyles']); + } +} + +export interface MarkdownContributionProvider { + readonly extensionPath: string; + readonly contributions: MarkdownContributions; +} + +class VSCodeExtensionMarkdownContributionProvider implements MarkdownContributionProvider { + private _contributions?: MarkdownContributions; public constructor( public readonly extensionPath: string, ) { } - public get previewScripts(): vscode.Uri[] { - this.ensureLoaded(); - return this._scripts; - } - - public get previewStyles(): vscode.Uri[] { - this.ensureLoaded(); - return this._styles; - } - - public get previewResourceRoots(): vscode.Uri[] { - this.ensureLoaded(); - return this._previewResourceRoots; - } - - public get markdownItPlugins(): Thenable<(md: any) => any>[] { - this.ensureLoaded(); - return this._plugins; - } - - private ensureLoaded() { - if (this._loaded) { - return; + public get contributions(): MarkdownContributions { + if (!this._contributions) { + this._contributions = vscode.extensions.all.reduce( + (contributions, extension) => MarkdownContributions.merge(contributions, MarkdownContributions.fromExtension(extension)), + MarkdownContributions.Empty); } - - this._loaded = true; - for (const extension of vscode.extensions.all) { - const contributes = extension.packageJSON && extension.packageJSON.contributes; - if (!contributes) { - continue; - } - - this.tryLoadPreviewStyles(contributes, extension); - this.tryLoadPreviewScripts(contributes, extension); - this.tryLoadMarkdownItPlugins(contributes, extension); - - if (contributes['markdown.previewScripts'] || contributes['markdown.previewStyles']) { - this._previewResourceRoots.push(vscode.Uri.file(extension.extensionPath)); - } - } - } - - private tryLoadMarkdownItPlugins( - contributes: any, - extension: vscode.Extension - ) { - if (contributes['markdown.markdownItPlugins']) { - this._plugins.push(extension.activate().then(() => { - if (extension.exports && extension.exports.extendMarkdownIt) { - return (md: any) => extension.exports.extendMarkdownIt(md); - } - return (md: any) => md; - })); - } - } - - private tryLoadPreviewScripts( - contributes: any, - extension: vscode.Extension - ) { - this._scripts.push(...resolveExtensionResources(extension, contributes['markdown.previewScripts'])); - } - - private tryLoadPreviewStyles( - contributes: any, - extension: vscode.Extension - ) { - this._styles.push(...resolveExtensionResources(extension, contributes['markdown.previewStyles'])); + return this._contributions; } } -export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributions { - return new MarkdownExtensionContributions(context.extensionPath); +export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider { + return new VSCodeExtensionMarkdownContributionProvider(context.extensionPath); } \ No newline at end of file diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index a1834e057a2..1a674c8a509 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -3,17 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { MarkdownContributions } from '../markdownExtensions'; +import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { githubSlugifier } from '../slugify'; -const emptyContributions = new class implements MarkdownContributions { +const emptyContributions = new class implements MarkdownContributionProvider { readonly extensionPath = ''; - readonly previewScripts: vscode.Uri[] = []; - readonly previewStyles: vscode.Uri[] = []; - readonly previewResourceRoots: vscode.Uri[] = []; - readonly markdownItPlugins: Promise<(md: any) => any>[] = []; + readonly contributions = MarkdownContributions.Empty; }; export function createNewMarkdownEngine(): MarkdownEngine { From 68df4365cea420f7c391782bbdd4d834b6156c52 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Tue, 5 Feb 2019 17:03:28 -0800 Subject: [PATCH 1039/1066] adding telemtetry to detect machine id guids (#67955) * adding telemtetry to detect machine id guids * better condition * stricter * fix for regex --- src/vs/platform/telemetry/common/telemetryService.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 7f300844819..3ed960f6a6b 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -61,6 +61,17 @@ export class TelemetryService implements ITelemetryService { */ this.publicLog('optInStatus', { optIn: this._userOptIn }); } + + this._commonProperties.then(values => { + const isHashedId = /^[a-f0-9]+$/i.test(values['common.machineId']); + + /* __GDPR__ + "machineIdFallback" : { + "usingFallbackGuid" : { "classification": "SystemMetaData", "purpose": "BusinessInsight", "isMeasurement": true } + } + */ + this.publicLog('machineIdFallback', { usingFallbackGuid: !isHashedId }); + }); } private _updateUserOptIn(): void { From 8f10101dd42298e42e9de40b2c0f8d0da8c46c2d Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 17:08:57 -0800 Subject: [PATCH 1040/1066] Create base class for disposable objects --- .../src/features/preview.ts | 25 +++++++------ .../src/features/workspaceSymbolProvider.ts | 35 ++++++------------- .../src/util/dispose.ts | 26 ++++++++++++++ 3 files changed, 48 insertions(+), 38 deletions(-) diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 4c0d0911296..4ecef4ac7b6 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -8,7 +8,7 @@ import * as path from 'path'; import { Logger } from '../logger'; import { MarkdownContentProvider } from './previewContentProvider'; -import { disposeAll } from '../util/dispose'; +import { Disposable } from '../util/dispose'; import * as nls from 'vscode-nls'; import { getVisibleLine, MarkdownFileTopmostLineMonitor } from '../util/topmostLineMonitor'; @@ -60,7 +60,7 @@ interface PreviewStyleLoadErrorMessage extends WebviewMessage { }; } -export class MarkdownPreview { +export class MarkdownPreview extends Disposable { public static viewType = 'markdown.preview'; @@ -70,7 +70,6 @@ export class MarkdownPreview { private readonly editor: vscode.WebviewPanel; private throttleTimer: any; private line: number | undefined = undefined; - private readonly disposables: vscode.Disposable[] = []; private firstUpdate = true; private currentVersion?: { resource: vscode.Uri, version: number }; private forceUpdate = false; @@ -149,17 +148,18 @@ export class MarkdownPreview { topmostLineMonitor: MarkdownFileTopmostLineMonitor, private readonly _contributionProvider: MarkdownContributionProvider, ) { + super(); this._resource = resource; this._locked = locked; this.editor = webview; this.editor.onDidDispose(() => { this.dispose(); - }, null, this.disposables); + }, null, this._disposables); this.editor.onDidChangeViewState(e => { this._onDidChangeViewStateEmitter.fire(e); - }, null, this.disposables); + }, null, this._disposables); this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { @@ -191,19 +191,19 @@ export class MarkdownPreview { vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", e.body.unloadedStyles.join(', '))); break; } - }, null, this.disposables); + }, null, this._disposables); vscode.workspace.onDidChangeTextDocument(event => { if (this.isPreviewOf(event.document.uri)) { this.refresh(); } - }, null, this.disposables); + }, null, this._disposables); topmostLineMonitor.onDidChangeTopmostLine(event => { if (this.isPreviewOf(event.resource)) { this.updateForView(event.resource, event.line); } - }, null, this.disposables); + }, null, this._disposables); vscode.window.onDidChangeTextEditorSelection(event => { if (this.isPreviewOf(event.textEditor.document.uri)) { @@ -213,13 +213,13 @@ export class MarkdownPreview { source: this.resource.toString() }); } - }, null, this.disposables); + }, null, this._disposables); vscode.window.onDidChangeActiveTextEditor(editor => { if (editor && isMarkdownFile(editor.document) && !this._locked) { this.update(editor.document.uri); } - }, null, this.disposables); + }, null, this._disposables); } private readonly _onDisposeEmitter = new vscode.EventEmitter(); @@ -242,18 +242,17 @@ export class MarkdownPreview { } public dispose() { + super.dispose(); if (this._disposed) { return; } this._disposed = true; this._onDisposeEmitter.fire(); - this._onDisposeEmitter.dispose(); + this._onDidChangeViewStateEmitter.dispose(); this.editor.dispose(); - - disposeAll(this.disposables); } public update(resource: vscode.Uri) { diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index 12e51c75d76..73e04269fce 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { disposeAll } from '../util/dispose'; +import { Disposable } from '../util/dispose'; import { isMarkdownFile } from '../util/file'; import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; @@ -18,25 +18,13 @@ export interface WorkspaceMarkdownDocumentProvider { readonly onDidDeleteMarkdownDocument: vscode.Event; } -class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocumentProvider { +class VSCodeWorkspaceMarkdownDocumentProvider extends Disposable implements WorkspaceMarkdownDocumentProvider { - private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter(); - private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter(); - private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter(); + private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); + private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); + private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); private _watcher: vscode.FileSystemWatcher | undefined; - private _disposables: vscode.Disposable[] = []; - - public dispose() { - this._onDidChangeMarkdownDocumentEmitter.dispose(); - this._onDidDeleteMarkdownDocumentEmitter.dispose(); - - if (this._watcher) { - this._watcher.dispose(); - } - - disposeAll(this._disposables); - } async getAllMarkdownDocuments() { const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); @@ -64,7 +52,7 @@ class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocume return; } - this._watcher = vscode.workspace.createFileSystemWatcher('**/*.md'); + this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md')); this._watcher.onDidChange(async resource => { const document = await this.getMarkdownDocument(resource); @@ -98,15 +86,16 @@ class VSCodeWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocume } -export default class MarkdownWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { +export default class MarkdownWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider { private _symbolCache = new Map>>(); private _symbolCachePopulated: boolean = false; - private _disposables: vscode.Disposable[] = []; public constructor( private _symbolProvider: MDDocumentSymbolProvider, private _workspaceMarkdownDocumentProvider: WorkspaceMarkdownDocumentProvider = new VSCodeWorkspaceMarkdownDocumentProvider() - ) { } + ) { + super(); + } public async provideWorkspaceSymbols(query: string): Promise { if (!this._symbolCachePopulated) { @@ -130,10 +119,6 @@ export default class MarkdownWorkspaceSymbolProvider implements vscode.Workspace } } - public dispose(): void { - disposeAll(this._disposables); - } - private getSymbols(document: SkinnyTextDocument): Lazy> { return lazy(async () => { return this._symbolProvider.provideDocumentSymbolInformation(document); diff --git a/extensions/markdown-language-features/src/util/dispose.ts b/extensions/markdown-language-features/src/util/dispose.ts index 19b3b3f4f19..548094c28e5 100644 --- a/extensions/markdown-language-features/src/util/dispose.ts +++ b/extensions/markdown-language-features/src/util/dispose.ts @@ -14,3 +14,29 @@ export function disposeAll(disposables: vscode.Disposable[]) { } } +export abstract class Disposable { + private _isDisposed = false; + + protected _disposables: vscode.Disposable[] = []; + + public dispose(): any { + if (this._isDisposed) { + return; + } + this._isDisposed = true; + disposeAll(this._disposables); + } + + protected _register(value: T): T { + if (this._isDisposed) { + value.dispose(); + } else { + this._disposables.push(value); + } + return value; + } + + protected get isDisposed() { + return this._isDisposed; + } +} \ No newline at end of file From 038e8d39046fd93923785f9bcfe9d8073fe6a4f1 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Feb 2019 17:44:57 -0800 Subject: [PATCH 1041/1066] Make markdown extension points dynamic For #67574 Blocked by #67958 --- .../src/features/preview.ts | 4 ++ .../src/markdownEngine.ts | 9 +++- .../src/markdownExtensions.ts | 50 +++++++++++++++---- .../src/test/engine.ts | 2 + .../src/util/arrays.ts | 18 +++++++ 5 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 extensions/markdown-language-features/src/util/arrays.ts diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 4ecef4ac7b6..d9bcd0bb00d 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -161,6 +161,10 @@ export class MarkdownPreview extends Disposable { this._onDidChangeViewStateEmitter.fire(e); }, null, this._disposables); + _contributionProvider.onContributionsChanged(() => { + setImmediate(() => this.refresh()); + }, null, this._disposables); + this.editor.webview.onDidReceiveMessage((e: CacheImageSizesMessage | RevealLineMessage | DidClickMessage | ClickLinkMessage | ShowPreviewSecuritySelectorMessage | PreviewStyleLoadErrorMessage) => { if (e.source !== this._resource.toString()) { return; diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 090c8ab0da4..c26eb4834b8 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -59,14 +59,19 @@ export class MarkdownEngine { public constructor( private readonly contributionProvider: MarkdownContributionProvider, private readonly slugifier: Slugifier, - ) { } + ) { + contributionProvider.onContributionsChanged(() => { + // Markdown plugin contributions may have changed + this.md = undefined; + }); + } private async getEngine(config: MarkdownItConfig): Promise { if (!this.md) { this.md = import('markdown-it').then(async markdownIt => { let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); - for (const plugin of this.contributionProvider.contributions.markdownItPlugins) { + for (const plugin of this.contributionProvider.contributions.markdownItPlugins.values()) { try { md = (await plugin)(md); } catch { diff --git a/extensions/markdown-language-features/src/markdownExtensions.ts b/extensions/markdown-language-features/src/markdownExtensions.ts index 4ca972f5c70..567ee946b9d 100644 --- a/extensions/markdown-language-features/src/markdownExtensions.ts +++ b/extensions/markdown-language-features/src/markdownExtensions.ts @@ -5,6 +5,8 @@ import * as vscode from 'vscode'; import * as path from 'path'; +import { Disposable } from './util/dispose'; +import * as arrays from './util/arrays'; const resolveExtensionResource = (extension: vscode.Extension, resourcePath: string): vscode.Uri => { return vscode.Uri.file(path.join(extension.extensionPath, resourcePath)) @@ -29,7 +31,7 @@ export interface MarkdownContributions { readonly previewScripts: ReadonlyArray; readonly previewStyles: ReadonlyArray; readonly previewResourceRoots: ReadonlyArray; - readonly markdownItPlugins: ReadonlyArray any>>; + readonly markdownItPlugins: Map any>>; } export namespace MarkdownContributions { @@ -37,7 +39,7 @@ export namespace MarkdownContributions { previewScripts: [], previewStyles: [], previewResourceRoots: [], - markdownItPlugins: [] + markdownItPlugins: new Map() }; export function merge(a: MarkdownContributions, b: MarkdownContributions): MarkdownContributions { @@ -45,10 +47,21 @@ export namespace MarkdownContributions { previewScripts: [...a.previewScripts, ...b.previewScripts], previewStyles: [...a.previewStyles, ...b.previewStyles], previewResourceRoots: [...a.previewResourceRoots, ...b.previewResourceRoots], - markdownItPlugins: [...a.markdownItPlugins, ...b.markdownItPlugins], + markdownItPlugins: new Map([...a.markdownItPlugins.entries(), ...b.markdownItPlugins.entries()]), }; } + function uriEqual(a: vscode.Uri, b: vscode.Uri): boolean { + return a.toString() === b.toString(); + } + + export function equal(a: MarkdownContributions, b: MarkdownContributions): boolean { + return arrays.equals(a.previewScripts, b.previewScripts, uriEqual) + && arrays.equals(a.previewStyles, b.previewStyles, uriEqual) + && arrays.equals(a.previewResourceRoots, b.previewResourceRoots, uriEqual) + && arrays.equals(Array.from(a.markdownItPlugins.keys()), Array.from(b.markdownItPlugins.keys())); + } + export function fromExtension( extension: vscode.Extension ): MarkdownContributions { @@ -69,7 +82,7 @@ export namespace MarkdownContributions { previewScripts: scripts, previewStyles: styles, previewResourceRoots, - markdownItPlugins: plugins ? [plugins] : [] + markdownItPlugins: plugins ? new Map([[extension.id, plugins]]) : new Map() }; } @@ -106,23 +119,42 @@ export namespace MarkdownContributions { export interface MarkdownContributionProvider { readonly extensionPath: string; readonly contributions: MarkdownContributions; + readonly onContributionsChanged: vscode.Event; } -class VSCodeExtensionMarkdownContributionProvider implements MarkdownContributionProvider { +class VSCodeExtensionMarkdownContributionProvider extends Disposable implements MarkdownContributionProvider { private _contributions?: MarkdownContributions; public constructor( public readonly extensionPath: string, - ) { } + ) { + super(); + + vscode.extensions.onDidChange(() => { + const currentContributions = this.getCurrentContributions(); + const existingContributions = this._contributions || MarkdownContributions.Empty; + if (!MarkdownContributions.equal(existingContributions, currentContributions)) { + this._contributions = currentContributions; + this._onContributionsChanged.fire(this); + } + }, undefined, this._disposables); + } + + private readonly _onContributionsChanged = new vscode.EventEmitter(); + public readonly onContributionsChanged = this._onContributionsChanged.event; public get contributions(): MarkdownContributions { if (!this._contributions) { - this._contributions = vscode.extensions.all.reduce( - (contributions, extension) => MarkdownContributions.merge(contributions, MarkdownContributions.fromExtension(extension)), - MarkdownContributions.Empty); + this._contributions = this.getCurrentContributions(); } return this._contributions; } + + private getCurrentContributions(): MarkdownContributions { + return vscode.extensions.all + .map(MarkdownContributions.fromExtension) + .reduce(MarkdownContributions.merge, MarkdownContributions.Empty); + } } export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributionProvider { diff --git a/extensions/markdown-language-features/src/test/engine.ts b/extensions/markdown-language-features/src/test/engine.ts index 1a674c8a509..fef334b4cf5 100644 --- a/extensions/markdown-language-features/src/test/engine.ts +++ b/extensions/markdown-language-features/src/test/engine.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; import { MarkdownContributionProvider, MarkdownContributions } from '../markdownExtensions'; import { githubSlugifier } from '../slugify'; @@ -10,6 +11,7 @@ import { githubSlugifier } from '../slugify'; const emptyContributions = new class implements MarkdownContributionProvider { readonly extensionPath = ''; readonly contributions = MarkdownContributions.Empty; + readonly onContributionsChanged = new vscode.EventEmitter().event; }; export function createNewMarkdownEngine(): MarkdownEngine { diff --git a/extensions/markdown-language-features/src/util/arrays.ts b/extensions/markdown-language-features/src/util/arrays.ts new file mode 100644 index 00000000000..10599259901 --- /dev/null +++ b/extensions/markdown-language-features/src/util/arrays.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. + *--------------------------------------------------------------------------------------------*/ + +export function equals(one: ReadonlyArray, other: ReadonlyArray, itemEquals: (a: T, b: T) => boolean = (a, b) => a === b): boolean { + if (one.length !== other.length) { + return false; + } + + for (let i = 0, len = one.length; i < len; i++) { + if (!itemEquals(one[i], other[i])) { + return false; + } + } + + return true; +} From 97680b78e4f246601d52836c04704608fae2a148 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Tue, 5 Feb 2019 18:53:23 -0800 Subject: [PATCH 1042/1066] fixes unit tests --- .../telemetry/common/telemetryService.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 3ed960f6a6b..87e94be9834 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -60,18 +60,18 @@ export class TelemetryService implements ITelemetryService { } */ this.publicLog('optInStatus', { optIn: this._userOptIn }); + + this._commonProperties.then(values => { + const isHashedId = /^[a-f0-9]+$/i.test(values['common.machineId']); + + /* __GDPR__ + "machineIdFallback" : { + "usingFallbackGuid" : { "classification": "SystemMetaData", "purpose": "BusinessInsight", "isMeasurement": true } + } + */ + this.publicLog('machineIdFallback', { usingFallbackGuid: !isHashedId }); + }); } - - this._commonProperties.then(values => { - const isHashedId = /^[a-f0-9]+$/i.test(values['common.machineId']); - - /* __GDPR__ - "machineIdFallback" : { - "usingFallbackGuid" : { "classification": "SystemMetaData", "purpose": "BusinessInsight", "isMeasurement": true } - } - */ - this.publicLog('machineIdFallback', { usingFallbackGuid: !isHashedId }); - }); } private _updateUserOptIn(): void { From ec081573ed2e53669932b64db167997364ada54e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Feb 2019 17:44:49 -0800 Subject: [PATCH 1043/1066] Restore settings editor focus sinks --- .../electron-browser/settingsEditor2.ts | 120 ++++++++++-------- 1 file changed, 68 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index ea9a1420deb..bee9a940ba4 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -488,57 +488,81 @@ export class SettingsEditor2 extends BaseEditor { this.createTOC(bodyContainer); - // this.createFocusSink( - // bodyContainer, - // e => { - // if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { - // if (this.settingsTree.getScrollPosition() > 0) { - // const firstElement = this.settingsTree.getFirstVisibleElement(); - // this.settingsTree.reveal(firstElement, 0.1); - // return true; - // } - // } else { - // const firstControl = this.settingsTree.getHTMLElement().querySelector(SettingsRenderer.CONTROL_SELECTOR); - // if (firstControl) { - // (firstControl).focus(); - // } - // } + this.createFocusSink( + bodyContainer, + e => { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { + if (this.settingsTree.scrollTop > 0) { + const firstElement = this.getFirstVisibleElement(); + this.settingsTree.reveal(firstElement, 0.1); + return true; + } + } else { + const firstControl = this.settingsTree.getHTMLElement().querySelector(AbstractSettingRenderer.CONTROL_SELECTOR); + if (firstControl) { + (firstControl).focus(); + } + } - // return false; - // }, - // 'settings list focus helper'); + return false; + }, + 'settings list focus helper'); this.createSettingsTree(bodyContainer); - // this.createFocusSink( - // bodyContainer, - // e => { - // if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { - // if (this.settingsTree.getScrollPosition() < 1) { - // const lastElement = this.settingsTree.getLastVisibleElement(); - // this.settingsTree.reveal(lastElement, 0.9); - // return true; - // } - // } + this.createFocusSink( + bodyContainer, + e => { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { + if (this.settingsTree.scrollTop < this.settingsTree.scrollHeight) { + const lastElement = this.getLastVisibleElement(); + this.settingsTree.reveal(lastElement, 0.9); + return true; + } + } - // return false; - // }, - // 'settings list focus helper' - // ); + return false; + }, + 'settings list focus helper' + ); } - // private createFocusSink(container: HTMLElement, callback: (e: any) => boolean, label: string): HTMLElement { - // const listFocusSink = DOM.append(container, $('.settings-tree-focus-sink')); - // listFocusSink.setAttribute('aria-label', label); - // listFocusSink.tabIndex = 0; - // this._register(DOM.addDisposableListener(listFocusSink, 'focus', (e: any) => { - // if (e.relatedTarget && callback(e)) { - // e.relatedTarget.focus(); - // } - // })); + private getFirstVisibleElement(nth = 0): SettingsTreeElement | null { + // Hack, see https://github.com/Microsoft/vscode/issues/64749 + const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); + const firstEl = settingItems[nth] || settingItems[0]; + if (!firstEl) { + return null; + } - // return listFocusSink; - // } + const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); + return this.settingsTreeModel.getElementById(firstSettingId); + } + + private getLastVisibleElement(): SettingsTreeElement | null { + // Hack, see https://github.com/Microsoft/vscode/issues/64749 + const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); + const firstEl = settingItems[settingItems.length - 1]; + if (!firstEl) { + return null; + } + + const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); + return this.settingsTreeModel.getElementById(firstSettingId); + } + + private createFocusSink(container: HTMLElement, callback: (e: any) => boolean, label: string): HTMLElement { + const listFocusSink = DOM.append(container, $('.settings-tree-focus-sink')); + listFocusSink.setAttribute('aria-label', label); + listFocusSink.tabIndex = 0; + this._register(DOM.addDisposableListener(listFocusSink, 'focus', (e: any) => { + if (e.relatedTarget && callback(e)) { + e.relatedTarget.focus(); + } + })); + + return listFocusSink; + } private createTOC(parent: HTMLElement): void { this.tocTreeModel = new TOCTreeModel(this.viewState); @@ -663,15 +687,7 @@ export class SettingsEditor2 extends BaseEditor { return; } - // Hack, see https://github.com/Microsoft/vscode/issues/64749 - const settingItems = this.settingsTree.getHTMLElement().querySelectorAll(AbstractSettingRenderer.CONTENTS_SELECTOR); - const firstEl = settingItems[1] || settingItems[0]; - if (!firstEl) { - return; - } - - const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(firstEl); - const elementToSync = this.settingsTreeModel.getElementById(firstSettingId); + const elementToSync = this.getFirstVisibleElement(1); const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : elementToSync instanceof SettingsTreeGroupElement ? elementToSync : null; From 00436419c7f9f17668a30f18dbfe24e0c559b810 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 08:43:47 +0100 Subject: [PATCH 1044/1066] fix panel drag image --- src/vs/base/browser/ui/list/list.css | 2 +- src/vs/base/browser/ui/list/listView.ts | 2 +- src/vs/base/browser/ui/list/listWidget.ts | 6 +++--- src/vs/base/browser/ui/splitview/panelview.ts | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 666bdaa9167..73756ce7811 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -60,7 +60,7 @@ } /* Dnd */ -.monaco-list-drag-image { +.monaco-drag-image { display: inline-block; padding: 1px 7px; border-radius: 10px; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 40c16806df2..a70fe3c8c84 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -729,7 +729,7 @@ export class ListView implements ISpliceable, IDisposable { label = String(elements.length); } - const dragImage = DOM.$('.monaco-list-drag-image'); + const dragImage = DOM.$('.monaco-drag-image'); dragImage.textContent = label; document.body.appendChild(dragImage); event.dataTransfer.setDragImage(dragImage, -10, -10); diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 4724ca6224d..ec06bc2dcb5 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -695,14 +695,14 @@ export class DefaultStyleController implements IStyleController { if (styles.listFocusAndSelectionBackground) { content.push(` - .monaco-list-drag-image, + .monaco-drag-image, .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-list-drag-image, + .monaco-drag-image, .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -735,7 +735,7 @@ export class DefaultStyleController implements IStyleController { if (styles.listFocusOutline) { content.push(` - .monaco-list-drag-image, + .monaco-drag-image, .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 18fecbbaafa..22736a15504 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -268,7 +268,7 @@ class PanelDraggable extends Disposable { e.dataTransfer.effectAllowed = 'move'; - const dragImage = append(document.body, $('.monaco-panel-drag-image', {}, this.panel.draggableElement.textContent || '')); + const dragImage = append(document.body, $('.monaco-drag-image', {}, this.panel.draggableElement.textContent || '')); e.dataTransfer.setDragImage(dragImage, -10, -10); setTimeout(() => document.body.removeChild(dragImage), 0); @@ -336,6 +336,7 @@ class PanelDraggable extends Disposable { backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); } + console.log(backgroundColor); this.panel.dropTargetElement.style.backgroundColor = backgroundColor; } } From 4fb6b7093afc09c52cafff60f3125d253ab4473b Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 08:47:03 +0100 Subject: [PATCH 1045/1066] remove console.log --- src/vs/base/browser/ui/splitview/panelview.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/base/browser/ui/splitview/panelview.ts b/src/vs/base/browser/ui/splitview/panelview.ts index 22736a15504..bc375e34a37 100644 --- a/src/vs/base/browser/ui/splitview/panelview.ts +++ b/src/vs/base/browser/ui/splitview/panelview.ts @@ -336,7 +336,6 @@ class PanelDraggable extends Disposable { backgroundColor = (this.panel.dropBackground || PanelDraggable.DefaultDragOverBackgroundColor).toString(); } - console.log(backgroundColor); this.panel.dropTargetElement.style.backgroundColor = backgroundColor; } } From d04106a7807f81e2ee2b6476aadb1e71fef5bef0 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 08:57:29 +0100 Subject: [PATCH 1046/1066] open editors shouldn't accept every drag and drop --- .../electron-browser/views/openEditorsView.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 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 dedd674663a..58e99c7a4e0 100644 --- a/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts +++ b/src/vs/workbench/parts/files/electron-browser/views/openEditorsView.ts @@ -34,12 +34,12 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions'; import { DirtyEditorContext, OpenEditorsGroupContext } from 'vs/workbench/parts/files/electron-browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; -import { ResourcesDropHandler, fillResourceDataTransfers } from 'vs/workbench/browser/dnd'; +import { ResourcesDropHandler, fillResourceDataTransfers, CodeDataTransfers } from 'vs/workbench/browser/dnd'; import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { IDragAndDropData } from 'vs/base/browser/dnd'; +import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; -import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; +import { ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { URI } from 'vs/base/common/uri'; const $ = dom.$; @@ -645,6 +645,18 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop Date: Wed, 6 Feb 2019 09:05:48 +0100 Subject: [PATCH 1047/1066] electron@3.1.3 --- .yarnrc | 2 +- cgmanifest.json | 4 ++-- src/typings/electron.d.ts | 7 ++++++- test/smoke/package.json | 2 +- test/smoke/yarn.lock | 8 ++++---- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.yarnrc b/.yarnrc index 190252d15da..fe7090fa356 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "3.1.2" +target "3.1.3" runtime "electron" diff --git a/cgmanifest.json b/cgmanifest.json index b7e74e39456..21d78500903 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -73,12 +73,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "bb28fa8e8e797db249a66405146ad0501eaf411a" + "commitHash": "72ff292302f95bfe5b4e02085a7be56254b42b0e" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "3.1.2" + "version": "3.1.3" }, { "component": { diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 8f72a1bc565..50fb0d3e4f4 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 3.1.2 +// Type definitions for Electron 3.1.3 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -7869,6 +7869,11 @@ declare namespace Electron { * Should only be specified for checkbox or radio type menu items. */ checked?: boolean; + /** + * If false, the accelerator won't be registered with the system, but it will still + * be displayed. Defaults to true. + */ + registerAccelerator?: boolean; /** * Should be specified for submenu type menu items. If submenu is specified, the * type: 'submenu' can be omitted. If the value is not a then it will be diff --git a/test/smoke/package.json b/test/smoke/package.json index fe5adc19357..80351281340 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.2", + "electron": "3.1.3", "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 6ca02b80baf..d1e614c487a 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.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.2.tgz#e410b976c56fc2f783c3b0fb6d757e02eaeab902" - integrity sha512-B/mXRCN8jGBBx8dvtIgLyW+nE8i9y7K9G6wijU+cLoneqF5al9BgZA1l5xgZEiUrwTtt0cgXIWNwhStt7EDoQQ== +electron@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/electron/-/electron-3.1.3.tgz#848fa0fc62d131978cde15ec04ce195a08dc4155" + integrity sha512-Y1TbV5py2O0br0JVYh+ew1cW4cIOOgRNRMzwTwWuZNMZ9fK/XLlqsbZr1GpYHdiN2yIU1koO+g4Cw8VuW86NXQ== dependencies: "@types/node" "^8.0.24" electron-download "^4.1.0" From 65f4cd7f35c4a89268cd7c2a8c31a1daa6c9df86 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 09:02:43 +0100 Subject: [PATCH 1048/1066] fix panel drop feedback in light and hc themes related to #67880 --- src/vs/workbench/common/theme.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index c5fc0beab31..7ca06839625 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -378,8 +378,8 @@ export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground' export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', { dark: Color.white.transparent(0.12), - light: Color.white.transparent(0.12), - hc: Color.white.transparent(0.12), + light: Color.black.transparent(0.1), + hc: Color.white.transparent(0.3), }, nls.localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', { From 987e4f929dcf89b5bb609ebee7b254cf94e1ab71 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 6 Feb 2019 09:12:24 +0100 Subject: [PATCH 1049/1066] argv: bring back deprecated ids (fixes #67937) --- src/vs/platform/environment/node/argv.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index d7e62cbfd82..10b166c3e01 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -22,6 +22,7 @@ export interface Option { id: string; type: 'boolean' | 'string'; alias?: string; + deprecates?: string; // old deprecated id args?: string | string[]; description?: string; cat?: keyof HelpCategories; @@ -41,7 +42,7 @@ export const options: Option[] = [ { id: 'folder-uri', type: 'string', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") }, { id: 'file-uri', type: 'string', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") }, - { id: 'extensions-dir', type: 'string', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, + { id: 'extensions-dir', type: 'string', deprecates: 'extensionHomePath', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") }, { id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") }, { id: 'show-versions', type: 'boolean', cat: 'e', description: localize('showVersions', "Show versions of installed extensions, when using --list-extension.") }, { id: 'install-extension', type: 'string', cat: 'e', args: 'extension-id', description: localize('installExtension', "Installs or updates the extension. Use `--force` argument to avoid prompts.") }, @@ -53,7 +54,7 @@ export const options: Option[] = [ { id: 'status', type: 'boolean', alias: 's', cat: 't', description: localize('status', "Print process usage and diagnostics information.") }, { id: 'prof-modules', type: 'boolean', alias: 'p', cat: 't', description: localize('prof-modules', "Capture performance markers while loading JS modules and print them with 'F1 > Developer: Startup Performance") }, { id: 'prof-startup', type: 'boolean', cat: 't', description: localize('prof-startup', "Run CPU profiler during startup") }, - { id: 'disable-extensions', type: 'boolean', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, + { id: 'disable-extensions', type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, { id: 'disable-extension', type: 'string', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") }, { id: 'inspect-extensions', type: 'string', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, @@ -66,8 +67,8 @@ export const options: Option[] = [ { id: 'extensionDevelopmentPath', type: 'string' }, { id: 'extensionTestsPath', type: 'string' }, { id: 'debugId', type: 'string' }, - { id: 'inspect-search', type: 'string' }, - { id: 'inspect-brk-extensions', type: 'string' }, + { id: 'inspect-search', type: 'string', deprecates: 'debugSearch' }, + { id: 'inspect-brk-extensions', type: 'string', deprecates: 'debugBrkSearch' }, { id: 'export-default-configuration', type: 'string' }, { id: 'install-source', type: 'string' }, { id: 'driver', type: 'string' }, @@ -101,9 +102,12 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru const string: string[] = []; const boolean: string[] = []; for (let o of options) { - if (o.alias && isOptionSupported(o)) { - alias[o.id] = o.alias; + if (isOptionSupported(o)) { + if (o.alias) { + alias[o.id] = o.alias; + } } + if (o.type === 'string') { string.push(o.id); } else if (o.type === 'boolean') { @@ -116,6 +120,10 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru if (o.alias) { delete parsedArgs[o.alias]; } + if (o.deprecates && parsedArgs[o.deprecates] && !parsedArgs[o.id]) { + parsedArgs[o.id] = parsedArgs[o.deprecates]; + delete parsedArgs[o.deprecates]; + } } return parsedArgs; } From b81b84ff639af8de8df7c53a67874c2aee83adfa Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 08:16:57 +0100 Subject: [PATCH 1050/1066] use rtf licenses --- build/win32/code.iss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/win32/code.iss b/build/win32/code.iss index 43572199eb5..ea31a50c9bf 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -1,7 +1,7 @@ #define LocalizedLanguageFile(Language = "") \ DirExists(RepoDir + "\licenses") && Language != "" \ - ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.txt"') \ - : '; LicenseFile: "' + RepoDir + '\LICENSE.txt"' + ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.rtf"') \ + : '; LicenseFile: "' + RepoDir + '\LICENSE.rtf"' [Setup] AppId={#AppId} From fad6ab69481bb19bad811adc24a78cb55acc1313 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 08:19:55 +0100 Subject: [PATCH 1051/1066] update distro --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 21e96b6c067..57926e29003 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.32.0", - "distro": "d274a23e463c080a0a3ba6278d43431d187e05f5", + "distro": "310c6547347fcd3b799dc67e048b7e010339211f", "author": { "name": "Microsoft Corporation" }, @@ -150,4 +150,4 @@ "windows-mutex": "0.2.1", "windows-process-tree": "0.2.3" } -} +} \ No newline at end of file From 20040a71b26129240e3c2baaf9e717410a7bbfaf Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 6 Feb 2019 11:02:29 +0100 Subject: [PATCH 1052/1066] argv: use 'deprecates' for debug(Brk)PluginHost and debug(Brk)Search --- src/vs/code/electron-main/window.ts | 4 ++-- src/vs/code/electron-main/windows.ts | 4 ++-- .../environment/common/environment.ts | 8 ++++---- src/vs/platform/environment/node/argv.ts | 19 +++++++++++-------- .../environment/node/environmentService.ts | 4 ++-- .../test/node/environmentService.test.ts | 12 ++++++++++++ 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index af7b8265a45..03d22708966 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -545,8 +545,8 @@ export class CodeWindow extends Disposable implements ICodeWindow { // in extension development mode. These options are all development related. if (this.isExtensionDevelopmentHost && cli) { configuration.verbose = cli.verbose; - configuration.debugPluginHost = cli.debugPluginHost; - configuration.debugBrkPluginHost = cli.debugBrkPluginHost; + configuration['inspect-extensions'] = cli['inspect-extensions']; + configuration['inspect-brk-extensions'] = cli['inspect-brk-extensions']; configuration.debugId = cli.debugId; configuration['extensions-dir'] = cli['extensions-dir']; } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 59bec7973b6..f51a81ec7db 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1282,9 +1282,9 @@ export class WindowsManager implements IWindowsMainService { if (!configuration.extensionDevelopmentPath && currentWindowConfig && !!currentWindowConfig.extensionDevelopmentPath) { configuration.extensionDevelopmentPath = currentWindowConfig.extensionDevelopmentPath; configuration.verbose = currentWindowConfig.verbose; - configuration.debugBrkPluginHost = currentWindowConfig.debugBrkPluginHost; + configuration['inspect-brk-extensions'] = currentWindowConfig['inspect-brk-extensions']; configuration.debugId = currentWindowConfig.debugId; - configuration.debugPluginHost = currentWindowConfig.debugPluginHost; + configuration['inspect-extensions'] = currentWindowConfig['inspect-extensions']; configuration['extensions-dir'] = currentWindowConfig['extensions-dir']; } } diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 23e84ca2f83..93f117ad29e 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -39,11 +39,11 @@ export interface ParsedArgs { 'builtin-extensions-dir'?: string; extensionDevelopmentPath?: string; extensionTestsPath?: string; - debugPluginHost?: string; - debugBrkPluginHost?: string; + 'inspect-extensions'?: string; + 'inspect-brk-extensions'?: string; debugId?: string; - debugSearch?: string; - debugBrkSearch?: string; + 'inspect-search'?: string; + 'inspect-brk-search'?: string; 'disable-extensions'?: boolean; 'disable-extension'?: string | string[]; 'list-extensions'?: boolean; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 10b166c3e01..054a7fe2c61 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -57,8 +57,8 @@ export const options: Option[] = [ { id: 'disable-extensions', type: 'boolean', deprecates: 'disableExtensions', cat: 't', description: localize('disableExtensions', "Disable all installed extensions.") }, { id: 'disable-extension', type: 'string', cat: 't', args: 'extension-id', description: localize('disableExtension', "Disable an extension.") }, - { id: 'inspect-extensions', type: 'string', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, - { id: 'inspect-brk-search', type: 'string', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, + { id: 'inspect-extensions', type: 'string', deprecates: 'debugPluginHost', args: 'port', cat: 't', description: localize('inspect-extensions', "Allow debugging and profiling of extensions. Check the developer tools for the connection URI.") }, + { id: 'inspect-brk-extensions', type: 'string', deprecates: 'debugBrkPluginHost', args: 'port', cat: 't', description: localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI.") }, { id: 'disable-gpu', type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") }, { id: 'upload-logs', type: 'string', cat: 't', description: localize('uploadLogs', "Uploads logs from current session to a secure endpoint.") }, { id: 'max-memory', type: 'boolean', cat: 't', description: localize('maxMemory', "Max memory size for a window (in Mbytes).") }, @@ -68,7 +68,7 @@ export const options: Option[] = [ { id: 'extensionTestsPath', type: 'string' }, { id: 'debugId', type: 'string' }, { id: 'inspect-search', type: 'string', deprecates: 'debugSearch' }, - { id: 'inspect-brk-extensions', type: 'string', deprecates: 'debugBrkSearch' }, + { id: 'inspect-brk-search', type: 'string', deprecates: 'debugBrkSearch' }, { id: 'export-default-configuration', type: 'string' }, { id: 'install-source', type: 'string' }, { id: 'driver', type: 'string' }, @@ -91,10 +91,7 @@ export const options: Option[] = [ { id: 'force', type: 'boolean' }, { id: 'trace-category-filter', type: 'string' }, { id: 'trace-options', type: 'string' }, - { id: 'prof-code-loading', type: 'boolean' }, - - { id: 'debugPluginHost', type: 'string', alias: 'inspect-extensions' }, - { id: 'debugBrkPluginHost', type: 'string', alias: 'inspect-brk-extensions' } + { id: 'prof-code-loading', type: 'boolean' } ]; export function parseArgs(args: string[], isOptionSupported = (_: Option) => true): ParsedArgs { @@ -110,8 +107,14 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru if (o.type === 'string') { string.push(o.id); + if (o.deprecates) { + string.push(o.deprecates); + } } else if (o.type === 'boolean') { boolean.push(o.id); + if (o.deprecates) { + boolean.push(o.deprecates); + } } } // remote aliases to avoid confusion @@ -120,7 +123,7 @@ export function parseArgs(args: string[], isOptionSupported = (_: Option) => tru if (o.alias) { delete parsedArgs[o.alias]; } - if (o.deprecates && parsedArgs[o.deprecates] && !parsedArgs[o.id]) { + if (o.deprecates && parsedArgs.hasOwnProperty(o.deprecates) && !parsedArgs[o.id]) { parsedArgs[o.id] = parsedArgs[o.deprecates]; delete parsedArgs[o.deprecates]; } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 5ecf265cdbd..8c33dbaec1e 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -250,11 +250,11 @@ export class EnvironmentService implements IEnvironmentService { } export function parseExtensionHostPort(args: ParsedArgs, isBuild: boolean): IExtensionHostDebugParams { - return parseDebugPort(args.debugPluginHost, args.debugBrkPluginHost, 5870, isBuild, args.debugId); + return parseDebugPort(args['inspect-extensions'], args['inspect-brk-extensions'], 5870, isBuild, args.debugId); } export function parseSearchPort(args: ParsedArgs, isBuild: boolean): IDebugParams { - return parseDebugPort(args.debugSearch, args.debugBrkSearch, 5876, isBuild); + return parseDebugPort(args['inspect-search'], args['inspect-brk-search'], 5876, isBuild); } export function parseDebugPort(debugArg: string | undefined, debugBrkArg: string | undefined, defaultBuildPort: number, isBuild: boolean, debugId?: string): IExtensionHostDebugParams { diff --git a/src/vs/platform/environment/test/node/environmentService.test.ts b/src/vs/platform/environment/test/node/environmentService.test.ts index b29c8629002..fc1faf3fff7 100644 --- a/src/vs/platform/environment/test/node/environmentService.test.ts +++ b/src/vs/platform/environment/test/node/environmentService.test.ts @@ -19,6 +19,12 @@ suite('EnvironmentService', () => { assert.deepEqual(parse(['--debugBrkPluginHost']), { port: null, break: false, debugId: undefined }); assert.deepEqual(parse(['--debugBrkPluginHost=5678']), { port: 5678, break: true, debugId: undefined }); assert.deepEqual(parse(['--debugPluginHost=1234', '--debugBrkPluginHost=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' }); + + assert.deepEqual(parse(['--inspect-extensions']), { port: null, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-extensions=1234']), { port: 1234, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-brk-extensions']), { port: null, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-brk-extensions=5678']), { port: 5678, break: true, debugId: undefined }); + assert.deepEqual(parse(['--inspect-extensions=1234', '--inspect-brk-extensions=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' }); }); test('parseExtensionHostPort when unbuilt', () => { @@ -30,6 +36,12 @@ suite('EnvironmentService', () => { assert.deepEqual(parse(['--debugBrkPluginHost']), { port: 5870, break: false, debugId: undefined }); assert.deepEqual(parse(['--debugBrkPluginHost=5678']), { port: 5678, break: true, debugId: undefined }); assert.deepEqual(parse(['--debugPluginHost=1234', '--debugBrkPluginHost=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' }); + + assert.deepEqual(parse(['--inspect-extensions']), { port: 5870, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-extensions=1234']), { port: 1234, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-brk-extensions']), { port: 5870, break: false, debugId: undefined }); + assert.deepEqual(parse(['--inspect-brk-extensions=5678']), { port: 5678, break: true, debugId: undefined }); + assert.deepEqual(parse(['--inspect-extensions=1234', '--inspect-brk-extensions=5678', '--debugId=7']), { port: 5678, break: true, debugId: '7' }); }); test('userDataPath', () => { From 07180523d60ba9d12f3b0a38203ce7f5f8ef0850 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 11:06:42 +0100 Subject: [PATCH 1053/1066] fix abstract tree trait changes when splicing fixes #67890 --- src/vs/base/browser/ui/tree/abstractTree.ts | 97 ++++++++++----------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5e947eb2052..5a777136756 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -6,12 +6,12 @@ import 'vs/css!./media/tree'; import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget'; -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list'; +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass } from 'vs/base/browser/dom'; import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event'; import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble, TreeVisibility, TreeFilterResult, ITreeModelSpliceEvent } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IDragAndDropData, StaticDND, DragAndDropData } from 'vs/base/browser/dnd'; import { range } from 'vs/base/common/arrays'; @@ -700,6 +700,11 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly expandOnlyOnTwistieClick?: boolean; } +function dfs(node: ITreeNode, fn: (node: ITreeNode) => void): void { + fn(node); + node.children.forEach(child => dfs(child, fn)); +} + /** * The trait concept needs to exist at the tree level, because collapsed * tree nodes will not be known by the list. @@ -715,16 +720,14 @@ class Trait { private _nodeSet: Set> | undefined; private get nodeSet(): Set> { if (!this._nodeSet) { - this._nodeSet = new Set(); - - for (const node of this.nodes) { - this._nodeSet.add(node); - } + this._nodeSet = this.createNodeSet(); } return this._nodeSet; } + constructor(private identityProvider?: IIdentityProvider) { } + set(nodes: ITreeNode[], browserEvent?: UIEvent): void { this.nodes = [...nodes]; this.elements = undefined; @@ -746,19 +749,37 @@ class Trait { return this.nodeSet.has(node); } - remove(nodes: ITreeNode[]): void { - if (nodes.length === 0) { + onDidModelSplice({ insertedNodes, deletedNodes }: ITreeModelSpliceEvent): void { + if (!this.identityProvider) { + const set = this.createNodeSet(); + const visit = node => set.delete(node); + deletedNodes.forEach(node => dfs(node, visit)); + this.set(values(set)); return; } - const set = this.nodeSet; - const visit = (node: ITreeNode) => { - set.delete(node); - node.children.forEach(visit); - }; + const identityProvider = this.identityProvider; + const nodesByIdentity = new Map>(); + this.nodes.forEach(node => nodesByIdentity.set(identityProvider.getId(node.element).toString(), node)); - nodes.forEach(visit); - this.set(values(set)); + const toDeleteByIdentity = new Map>(); + const toRemoveSetter = node => toDeleteByIdentity.set(identityProvider.getId(node.element).toString(), node); + const toRemoveDeleter = node => toDeleteByIdentity.delete(identityProvider.getId(node.element).toString()); + deletedNodes.forEach(node => dfs(node, toRemoveSetter)); + insertedNodes.forEach(node => dfs(node, toRemoveDeleter)); + + toDeleteByIdentity.forEach((_, id) => nodesByIdentity.delete(id)); + this.set(values(nodesByIdentity)); + } + + private createNodeSet(): Set> { + const set = new Set>(); + + for (const node of this.nodes) { + set.add(node); + } + + return set; } } @@ -879,16 +900,16 @@ export abstract class AbstractTree implements IDisposable private renderers: TreeRenderer[]; private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; - private focus = new Trait(); - private selection = new Trait(); + private focus: Trait; + private selection: Trait; private eventBufferer = new EventBufferer(); private typeFilterController?: TypeFilterController; protected disposables: IDisposable[] = []; get onDidScroll(): Event { return this.view.onDidScroll; } - readonly onDidChangeFocus: Event> = this.eventBufferer.wrapEvent(this.focus.onDidChange); - readonly onDidChangeSelection: Event> = this.eventBufferer.wrapEvent(this.selection.onDidChange); + get onDidChangeFocus(): Event> { return this.eventBufferer.wrapEvent(this.focus.onDidChange); } + get onDidChangeSelection(): Event> { return this.eventBufferer.wrapEvent(this.selection.onDidChange); } get onDidOpen(): Event> { return Event.map(this.view.onDidOpen, asTreeEvent); } get onMouseClick(): Event> { return Event.map(this.view.onMouseClick, asTreeMouseEvent); } @@ -936,41 +957,17 @@ export abstract class AbstractTree implements IDisposable this.disposables.push(filter); } + this.focus = new Trait(_options.identityProvider); + this.selection = new Trait(_options.identityProvider); this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, { ...asListOptions(() => this.model, _options), tree: this }); this.model = this.createModel(this.view, _options); onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; - if (this.options.identityProvider) { - const identityProvider = this.options.identityProvider; - - this.model.onDidSplice(e => { - if (e.deletedNodes.length === 0) { - return; - } - - this.eventBufferer.bufferEvents(() => { - const map = new Map>(); - - for (const node of e.deletedNodes) { - map.set(identityProvider.getId(node.element).toString(), node); - } - - for (const node of e.insertedNodes) { - map.delete(identityProvider.getId(node.element).toString()); - } - - if (map.size === 0) { - return; - } - - const deletedNodes = values(map); - - this.focus.remove(deletedNodes); - this.selection.remove(deletedNodes); - }); - }, null, this.disposables); - } + this.model.onDidSplice(e => { + this.focus.onDidModelSplice(e); + this.selection.onDidModelSplice(e); + }, null, this.disposables); if (_options.keyboardSupport !== false) { const onKeyDown = Event.chain(this.view.onKeyDown) From 1cb6e26ab275bea397ba955c192a0ad972e38fd6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 11:39:19 +0100 Subject: [PATCH 1054/1066] es6 compile attempts --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/base/parts/ipc/node/ipc.ts | 4 ++-- src/vs/code/node/shellEnv.ts | 4 ++-- src/vs/code/node/wait.ts | 4 ++-- src/vs/editor/contrib/suggest/suggest.ts | 2 +- .../node/extensionGalleryService.ts | 4 ++-- .../keybinding/common/keybindingResolver.ts | 2 +- .../lifecycle/electron-main/lifecycleMain.ts | 2 +- src/vs/platform/node/zip.ts | 2 +- .../storage/node/storageMainService.ts | 4 ++-- .../api/electron-browser/mainThreadTask.ts | 2 +- .../workbench/api/node/extHostApiCommands.ts | 2 +- .../api/node/extHostLanguageFeatures.ts | 2 +- .../common/editor/resourceEditorInput.ts | 2 +- .../electron-browser/actions/helpActions.ts | 8 +++---- src/vs/workbench/electron-browser/main.ts | 4 ++-- .../workbench/electron-browser/workbench.ts | 4 ++-- .../parts/extensions/common/extensions.ts | 2 +- .../electron-browser/extensionEditor.ts | 2 +- .../electron-browser/extensionsActions.ts | 16 ++++++------- .../node/extensionsWorkbenchService.ts | 2 +- .../files/browser/editors/textFileEditor.ts | 2 +- .../output/electron-browser/outputServices.ts | 2 +- .../parts/search/browser/searchView.ts | 4 ++-- .../services/backup/node/backupFileService.ts | 6 ++--- .../files/electron-browser/fileService.ts | 2 +- .../electron-browser/remoteFileService.ts | 2 +- .../watcher/unix/chokidarWatcherService.ts | 2 +- .../test/browser/editorGroupsService.test.ts | 4 ++-- .../keybinding/common/keybindingEditing.ts | 4 ++-- .../textfile/common/textFileEditorModel.ts | 2 +- .../common/textModelResolverService.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 24 +++++++++---------- 33 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 5a777136756..d3eb4876738 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -241,7 +241,7 @@ class TreeRenderer implements IListRenderer): void { diff --git a/src/vs/base/parts/ipc/node/ipc.ts b/src/vs/base/parts/ipc/node/ipc.ts index efda16899f0..e0e7b9ad3ac 100644 --- a/src/vs/base/parts/ipc/node/ipc.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -690,7 +690,7 @@ export class IPCClient implements IChannelClient, IChannelSer export function getDelayedChannel(promise: Promise): T { return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { - return promise.then(c => c.call(command, arg, cancellationToken)); + return promise.then(c => c.call(command, arg, cancellationToken)); }, listen(event: string, arg?: any): Event { @@ -752,4 +752,4 @@ export class StaticRouter implements IClientRouter await Event.toPromise(hub.onDidChangeConnections); return await this.route(hub); } -} \ No newline at end of file +} diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 5ea15be5ecf..4f3f8022f46 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -9,7 +9,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { isWindows } from 'vs/base/common/platform'; function getUnixShellEnvironment(): Promise { - const promise = new Promise((resolve, reject) => { + const promise = new Promise((resolve, reject) => { const runAsNode = process.env['ELECTRON_RUN_AS_NODE']; const noAttach = process.env['ELECTRON_NO_ATTACH_CONSOLE']; const mark = generateUuid().replace(/-/g, '').substr(0, 12); @@ -66,7 +66,7 @@ function getUnixShellEnvironment(): Promise { }); // swallow errors - return promise.then(undefined, () => ({})); + return promise.catch(() => ({})); } diff --git a/src/vs/code/node/wait.ts b/src/vs/code/node/wait.ts index 3b1f1d0750e..6245e052a73 100644 --- a/src/vs/code/node/wait.ts +++ b/src/vs/code/node/wait.ts @@ -21,6 +21,6 @@ export function createWaitMarkerFile(verbose?: boolean): Promise { console.error(`Failed to create marker file for --wait: ${error}`); } - return Promise.resolve(); + return Promise.resolve(undefined); }); -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index 3d7e3b008cd..b068415b791 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -170,7 +170,7 @@ export function provideSuggestionItems( return hasResult || token.isCancellationRequested; }).then(() => { if (token.isCancellationRequested) { - return Promise.reject(canceled()); + return Promise.reject(canceled()); } return allSuggestions.sort(getSuggestionComparator(snippetConfig)); }); diff --git a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts index a72c577218f..d4f636b463f 100644 --- a/src/vs/platform/extensionManagement/node/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/node/extensionGalleryService.ts @@ -774,7 +774,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService { const headers = { 'Accept-Encoding': 'gzip' }; return this.getAsset(manifest, { headers }) .then(context => asJson(context)) - .then(manifest => manifest ? manifest.engines.vscode : Promise.reject('Error while reading manifest')); + .then(manifest => manifest ? manifest.engines.vscode : Promise.reject('Error while reading manifest')); } private getLastValidExtensionVersionReccursively(extension: IRawGalleryExtension, versions: IRawGalleryExtensionVersion[]): Promise { @@ -839,7 +839,7 @@ export function resolveMarketplaceHeaders(environmentService: IEnvironmentServic const marketplaceMachineIdFile = path.join(environmentService.userDataPath, 'machineid'); return readFile(marketplaceMachineIdFile, 'utf8') - .then(contents => isUUID(contents) ? contents : Promise.resolve(null), () => Promise.resolve(null) /* error reading ID file */) + .then(contents => isUUID(contents) ? contents : null, () => null /* error reading ID file */) .then(uuid => { if (!uuid) { uuid = generateUuid(); diff --git a/src/vs/platform/keybinding/common/keybindingResolver.ts b/src/vs/platform/keybinding/common/keybindingResolver.ts index 4e2ee339287..0d631c3a4de 100644 --- a/src/vs/platform/keybinding/common/keybindingResolver.ts +++ b/src/vs/platform/keybinding/common/keybindingResolver.ts @@ -311,7 +311,7 @@ export class KeybindingResolver { if (seenMap.has(id)) { return; } - seenMap.set(id); + seenMap.set(id, true); if (id[0] === '_' || id.indexOf('vscode.') === 0) { // private command return; } diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts index 28697f4c745..587c31f584e 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts @@ -233,7 +233,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { } }); - this.pendingWillShutdownPromise = Promise.all(joiners).then(undefined, err => this.logService.error(err)); + this.pendingWillShutdownPromise = Promise.all(joiners).then(() => undefined, err => this.logService.error(err)); return this.pendingWillShutdownPromise; } diff --git a/src/vs/platform/node/zip.ts b/src/vs/platform/node/zip.ts index be8629a236a..5696d1dec62 100644 --- a/src/vs/platform/node/zip.ts +++ b/src/vs/platform/node/zip.ts @@ -87,7 +87,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa } }); - return Promise.resolve(mkdirp(targetDirName, undefined, token)).then(() => new Promise((c, e) => { + return Promise.resolve(mkdirp(targetDirName, undefined, token)).then(() => new Promise((c, e) => { if (token.isCancellationRequested) { return; } diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index 72ae2f3222b..03653e4f891 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -236,7 +236,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic return import('vscode-sqlite3').then(sqlite3 => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const handleSuffixKey = (row, key: string, suffix: string) => { if (endsWith(key, suffix.toLowerCase())) { const value: string = row.value.toString('utf16le'); @@ -366,4 +366,4 @@ export class StorageMainService extends Disposable implements IStorageMainServic checkIntegrity(full: boolean): Promise { return this.storage.checkIntegrity(full); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/api/electron-browser/mainThreadTask.ts b/src/vs/workbench/api/electron-browser/mainThreadTask.ts index e4d3db05dfe..cbd8ea073e5 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadTask.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadTask.ts @@ -532,7 +532,7 @@ export class MainThreadTask implements MainThreadTaskShape { forEach(values.variables, (entry) => { partiallyResolvedVars.push(entry.value); }); - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks').then(resolvedVars => { let result = { process: undefined as string, diff --git a/src/vs/workbench/api/node/extHostApiCommands.ts b/src/vs/workbench/api/node/extHostApiCommands.ts index 9b5931a4bac..9565fbc108c 100644 --- a/src/vs/workbench/api/node/extHostApiCommands.ts +++ b/src/vs/workbench/api/node/extHostApiCommands.ts @@ -372,7 +372,7 @@ export class ExtHostApiCommands { return undefined; } if (value.rejectReason) { - return Promise.reject(new Error(value.rejectReason)); + return Promise.reject(new Error(value.rejectReason)); } return typeConverters.WorkspaceEdit.to(value); }); diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index 76b8b1fbe00..df50261a1e6 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -553,7 +553,7 @@ class RenameAdapter { if (rejectReason) { return { rejectReason, range: undefined, text: undefined }; } else { - return Promise.reject(err); + return Promise.reject(err); } }); } diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 8e9061c79ec..d61b126f22f 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -92,7 +92,7 @@ export class ResourceEditorInput extends EditorInput { ref.dispose(); this.modelReference = null; - return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); + return Promise.reject(new Error(`Unexpected model for ResourceInput: ${this.resource}`)); } return model; diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts index 85d39d64d7b..009ef59e1b0 100644 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ b/src/vs/workbench/electron-browser/actions/helpActions.ts @@ -161,7 +161,7 @@ export class OpenTwitterUrlAction extends Action { super(id, label); } - run(): Promise { + run(): Promise { if (product.twitterUrl) { window.open(product.twitterUrl); } @@ -182,7 +182,7 @@ export class OpenRequestFeatureUrlAction extends Action { super(id, label); } - run(): Promise { + run(): Promise { if (product.requestFeatureUrl) { window.open(product.requestFeatureUrl); } @@ -203,7 +203,7 @@ export class OpenLicenseUrlAction extends Action { super(id, label); } - run(): Promise { + run(): Promise { if (product.licenseUrl) { if (language) { const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; @@ -229,7 +229,7 @@ export class OpenPrivacyStatementUrlAction extends Action { super(id, label); } - run(): Promise { + run(): Promise { if (product.privacyStatementUrl) { if (language) { const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index 1a7ba1c6ca0..f97c9e7afdd 100644 --- a/src/vs/workbench/electron-browser/main.ts +++ b/src/vs/workbench/electron-browser/main.ts @@ -164,7 +164,7 @@ function createWorkspaceInitializationPayload(configuration: IWindowConfiguratio } // Single-folder workspace - let workspaceInitializationPayload: Promise = Promise.resolve(); + let workspaceInitializationPayload: Promise = Promise.resolve(undefined); if (configuration.folderUri) { workspaceInitializationPayload = resolveSingleFolderWorkspaceInitializationPayload(configuration.folderUri); } @@ -179,7 +179,7 @@ function createWorkspaceInitializationPayload(configuration: IWindowConfiguratio } else if (environmentService.isExtensionDevelopment) { id = 'ext-dev'; // extension development window never stores backups and is a singleton } else { - return Promise.reject(new Error('Unexpected window configuration without backupPath')); + return Promise.reject(new Error('Unexpected window configuration without backupPath')); } payload = { id } as IEmptyWorkspaceInitializationPayload; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index d82db96296d..54405b2aa0f 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -743,7 +743,7 @@ export class Workbench extends Disposable implements IPartService { return editorService.openEditors(editors); } - return Promise.resolve(); + return Promise.resolve(undefined); } const editorsToOpen = this.resolveEditorsToOpen(); @@ -1814,4 +1814,4 @@ export class Workbench extends Disposable implements IPartService { this.workbenchGrid.layout(); } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/common/extensions.ts b/src/vs/workbench/parts/extensions/common/extensions.ts index 4684950b9a0..3d3ec28b199 100644 --- a/src/vs/workbench/parts/extensions/common/extensions.ts +++ b/src/vs/workbench/parts/extensions/common/extensions.ts @@ -139,4 +139,4 @@ export class ExtensionContainers extends Disposable { } } } -} \ No newline at end of file +} diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts index 65574224a7d..826a108b8c5 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionEditor.ts @@ -604,7 +604,7 @@ export class ExtensionEditor extends BaseEditor { } return this.loadContents(() => this.extensionDependencies.get()) - .then(extensionDependencies => { + .then(extensionDependencies => { if (extensionDependencies) { const content = $('div', { class: 'subcontent' }); const scrollableContent = new DomScrollableElement(content, {}); diff --git a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts index 33ff76a0343..2998e2f0086 100644 --- a/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/parts/extensions/electron-browser/extensionsActions.ts @@ -180,7 +180,7 @@ export class InstallAction extends ExtensionAction { return this.install(this.extension); } - private install(extension: IExtension): Promise { + private install(extension: IExtension): Promise { return this.extensionsWorkbenchService.install(extension).then(null, err => { if (!extension.gallery) { return this.notificationService.error(err); @@ -940,7 +940,7 @@ export class UpdateAllAction extends Action { return Promise.all(this.outdated.map(e => this.install(e))); } - private install(extension: IExtension): Promise { + private install(extension: IExtension): Promise { return this.extensionsWorkbenchService.install(extension).then(undefined, err => { if (!extension.gallery) { return this.notificationService.error(err); @@ -984,16 +984,16 @@ export class ReloadAction extends ExtensionAction { this.enabled = false; this.tooltip = ''; if (!this.extension) { - return Promise.resolve(); + return Promise.resolve(undefined); } const state = this.extension.state; if (state === ExtensionState.Installing || state === ExtensionState.Uninstalling) { - return Promise.resolve(); + return Promise.resolve(undefined); } const installed = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0]; const local = this.extension.local || (installed && installed.local); if (local && local.manifest && local.manifest.contributes && local.manifest.contributes.localizations && local.manifest.contributes.localizations.length > 0) { - return Promise.resolve(); + return Promise.resolve(undefined); } return this.extensionService.getExtensions() .then(runningExtensions => this.computeReloadState(runningExtensions, installed)); @@ -1312,7 +1312,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { viewlet.focus(); const names = this.recommendations.map(({ extensionId }) => extensionId); return this.extensionWorkbenchService.queryGallery({ names, source: 'install-all-workspace-recommendations' }).then(pager => { - let installPromises: Promise[] = []; + let installPromises: Promise[] = []; let model = new PagedModel(pager); for (let i = 0; i < pager.total; i++) { installPromises.push(model.resolve(i, CancellationToken.None).then(e => { @@ -1779,7 +1779,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio }); } - private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise { + private getSelectionPosition(content: string, resource: URI, path: json.JSONPath): Promise { const tree = json.parseTree(content); const node = json.findNodeAtLocation(tree, path); if (node && node.parent && node.parent.children) { @@ -1798,7 +1798,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio }; }); } - return Promise.resolve(); + return Promise.resolve(undefined); } private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> { diff --git a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts index 6d25977a663..3a5c4e528c9 100644 --- a/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts +++ b/src/vs/workbench/parts/extensions/node/extensionsWorkbenchService.ts @@ -673,7 +673,7 @@ export class ExtensionsWorkbenchService implements IExtensionsWorkbenchService, installVersion(extension: IExtension, version: string): Promise { if (!(extension instanceof Extension)) { - return Promise.resolve(); + return Promise.resolve(extension); } if (!extension.gallery) { diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index dceca11ee4d..acc992e3cae 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -177,7 +177,7 @@ export class TextFileEditor extends BaseTextEditor { if ((error).fileOperationResult === FileOperationResult.FILE_IS_DIRECTORY) { this.openAsFolder(input); - return Promise.reject(new Error(nls.localize('openFolderError', "File is a directory"))); + return Promise.reject(new Error(nls.localize('openFolderError', "File is a directory"))); } // Offer to create a file from the error if we have a file not found and the name is valid diff --git a/src/vs/workbench/parts/output/electron-browser/outputServices.ts b/src/vs/workbench/parts/output/electron-browser/outputServices.ts index e83e6fe759d..586a275ebbc 100644 --- a/src/vs/workbench/parts/output/electron-browser/outputServices.ts +++ b/src/vs/workbench/parts/output/electron-browser/outputServices.ts @@ -340,7 +340,7 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann private updateInProgress: boolean = false; private etag: string = ''; - private loadModelPromise: Promise = Promise.resolve(); + private loadModelPromise: Promise = Promise.resolve(undefined); constructor( outputChannelDescriptor: IOutputChannelDescriptor, diff --git a/src/vs/workbench/parts/search/browser/searchView.ts b/src/vs/workbench/parts/search/browser/searchView.ts index b492c49c74f..2c616389d02 100644 --- a/src/vs/workbench/parts/search/browser/searchView.ts +++ b/src/vs/workbench/parts/search/browser/searchView.ts @@ -122,7 +122,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { private searchWithoutFolderMessageElement: HTMLElement; - private currentSearchQ = Promise.resolve(); + private currentSearchQ = Promise.resolve(); constructor( @IPartService partService: IPartService, @@ -1228,7 +1228,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel { this.currentSearchQ = this.currentSearchQ .then(() => this.doSearch(query, options, excludePatternText, includePatternText)) - .then(() => { }, () => { }); + .then(() => undefined, () => undefined); } private doSearch(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string): Thenable { diff --git a/src/vs/workbench/services/backup/node/backupFileService.ts b/src/vs/workbench/services/backup/node/backupFileService.ts index f0e28bc0975..3783534ba58 100644 --- a/src/vs/workbench/services/backup/node/backupFileService.ts +++ b/src/vs/workbench/services/backup/node/backupFileService.ts @@ -257,7 +257,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(backupResource); } - return Promise.resolve(); + return Promise.resolve(undefined); } backupResource(resource: Uri, content: ITextSnapshot, versionId?: number): Promise { @@ -273,7 +273,7 @@ export class InMemoryBackupFileService implements IBackupFileService { return Promise.resolve(createTextBufferFactoryFromSnapshot(snapshot)); } - return Promise.resolve(); + return Promise.resolve(undefined); } getWorkspaceFileBackups(): Promise { @@ -299,4 +299,4 @@ export class InMemoryBackupFileService implements IBackupFileService { private hashPath(resource: Uri): string { return crypto.createHash('md5').update(resource.fsPath).digest('hex'); } -} \ No newline at end of file +} diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index f6419c6094f..1a66c56808e 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -555,7 +555,7 @@ export class FileService extends Disposable implements IFileService { // 1.) check file for writing return this.checkFileBeforeWriting(absolutePath, options).then(exists => { - let createParentsPromise: Promise; + let createParentsPromise: Promise; if (exists) { createParentsPromise = Promise.resolve(); } else { diff --git a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts index 80078b6cade..7d58f3f5694 100644 --- a/src/vs/workbench/services/files/electron-browser/remoteFileService.ts +++ b/src/vs/workbench/services/files/electron-browser/remoteFileService.ts @@ -386,7 +386,7 @@ export class RemoteFileService extends FileService { return toDecodeStream(readable, decodeStreamOpts).then(data => { if (options.acceptTextOnly && data.detected.seemsBinary) { - return Promise.reject(new FileOperationError( + return Promise.reject(new FileOperationError( localize('fileBinaryError', "File seems to be binary and cannot be opened as text"), FileOperationResult.FILE_IS_BINARY, options diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts index ba4ada4cd68..4ea2032f5a6 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -242,7 +242,7 @@ export class ChokidarWatcherService implements IWatcherService { }); } - return Promise.resolve(); + return Promise.resolve(undefined); }); } }); 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 806b62a5e55..445826f891b 100644 --- a/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/group/test/browser/editorGroupsService.test.ts @@ -41,7 +41,7 @@ export class TestEditorInput extends EditorInput implements IFileEditorInput { constructor(private resource: URI) { super(); } getTypeId() { return 'testEditorInputForEditorGroupService'; } - resolve(): Promise { return Promise.resolve(); } + resolve(): Promise { return Promise.resolve(undefined); } matches(other: TestEditorInput): boolean { return other && this.resource.toString() === other.resource.toString() && other instanceof TestEditorInput; } setEncoding(encoding: string) { } getEncoding(): string { return null!; } @@ -751,4 +751,4 @@ suite('Editor groups service', () => { part.dispose(); }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 354a3b44f43..e832b4a11b0 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -220,11 +220,11 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding if (model.getValue()) { const parsed = this.parse(model); if (parsed.parseErrors.length) { - return Promise.reject(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again."))); + return Promise.reject(new Error(localize('parseErrors', "Unable to write to the keybindings configuration file. Please open it to correct errors/warnings in the file and try again."))); } if (parsed.result) { if (!isArray(parsed.result)) { - return Promise.reject(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again."))); + return Promise.reject(new Error(localize('errorInvalidConfiguration', "Unable to write to the keybindings configuration file. It has an object which is not of type Array. Please open the file to clean up and try again."))); } } else { const content = EOL + '[]'; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 9537d48d1f2..248ba255f0e 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -215,7 +215,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Unset flags const undo = this.setDirty(false); - let loadPromise: Promise; + let loadPromise: Promise; if (soft) { loadPromise = Promise.resolve(); } else { diff --git a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts index e274fce62fa..3b011295c08 100644 --- a/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts +++ b/src/vs/workbench/services/textmodelResolver/common/textModelResolverService.ts @@ -107,7 +107,7 @@ class ResourceModelCollection extends ReferenceCollection { if (!model) { - return Promise.reject(new Error('resource is not available')); + return Promise.reject(new Error('resource is not available')); } return model; diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index f902c08b46c..a28b4952f26 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -429,10 +429,10 @@ export class TestFileDialogService implements IFileDialogService { return Promise.resolve(0); } public showSaveDialog(_options: ISaveDialogOptions): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } public showOpenDialog(_options: IOpenDialogOptions): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } } @@ -1002,7 +1002,7 @@ export class TestCodeEditorService implements ICodeEditorService { setTransientModelProperty(_model: ITextModel, _key: string, _value: any): void { } getTransientModelProperty(_model: ITextModel, _key: string) { } getActiveCodeEditor(): ICodeEditor { return null; } - openCodeEditor(_input: IResourceInput, _source: ICodeEditor, _sideBySide?: boolean): Promise { return Promise.resolve(); } + openCodeEditor(_input: IResourceInput, _source: ICodeEditor, _sideBySide?: boolean): Promise { return Promise.resolve(undefined); } } export class TestWindowService implements IWindowService { @@ -1063,7 +1063,7 @@ export class TestWindowService implements IWindowService { } enterWorkspace(_path: URI): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } toggleFullScreen(): Promise { @@ -1075,7 +1075,7 @@ export class TestWindowService implements IWindowService { } getRecentlyOpened(): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } focusWindow(): Promise { @@ -1224,7 +1224,7 @@ export class TestWindowsService implements IWindowsService { } enterWorkspace(_windowId: number, _path: URI): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } toggleFullScreen(_windowId: number): Promise { @@ -1248,7 +1248,7 @@ export class TestWindowsService implements IWindowsService { } getRecentlyOpened(_windowId: number): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } focusWindow(_windowId: number): Promise { @@ -1260,7 +1260,7 @@ export class TestWindowsService implements IWindowsService { } isMaximized(_windowId: number): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } maximizeWindow(_windowId: number): Promise { @@ -1313,7 +1313,7 @@ export class TestWindowsService implements IWindowsService { } getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - return Promise.resolve(); + return Promise.resolve(undefined); } getWindowCount(): Promise { @@ -1372,15 +1372,15 @@ export class TestWindowsService implements IWindowsService { } showMessageBox(_windowId: number, _options: Electron.MessageBoxOptions): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } showSaveDialog(_windowId: number, _options: Electron.SaveDialogOptions): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } showOpenDialog(_windowId: number, _options: Electron.OpenDialogOptions): Promise { - return Promise.resolve(); + return Promise.resolve(undefined); } openAboutDialog(): Promise { From e63c7d08601b98fc48b90f08054cee6a1df85f3f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 11:51:18 +0100 Subject: [PATCH 1055/1066] Promise.resolve() -> Promise --- src/typings/es6-promise.d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/typings/es6-promise.d.ts b/src/typings/es6-promise.d.ts index f674baa6a87..2d3271e2848 100644 --- a/src/typings/es6-promise.d.ts +++ b/src/typings/es6-promise.d.ts @@ -44,7 +44,12 @@ declare namespace Promise { * Make a new promise from the thenable. * A thenable is promise-like in as far as it has a "then" method. */ - function resolve(value?: T | Thenable): Promise; + function resolve(value: T | Thenable): Promise; + + /** + * + */ + function resolve(): Promise; /** * Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error From e18eda4d683e085690bdb77d3ae74edc5a76750b Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 11:54:34 +0100 Subject: [PATCH 1056/1066] strict null trouble --- src/vs/platform/commands/common/commands.ts | 4 ++-- .../services/themes/electron-browser/fileIconThemeData.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/commands/common/commands.ts b/src/vs/platform/commands/common/commands.ts index 5a5bf2ca121..16c3a764a02 100644 --- a/src/vs/platform/commands/common/commands.ts +++ b/src/vs/platform/commands/common/commands.ts @@ -132,7 +132,7 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR export const NullCommandService: ICommandService = { _serviceBrand: undefined, onWillExecuteCommand: () => ({ dispose: () => { } }), - executeCommand() { - return Promise.resolve(undefined); + executeCommand() { + return Promise.resolve(undefined); } }; diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts index 12655d450e1..7694bf4c616 100644 --- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts @@ -29,15 +29,15 @@ export class FileIconThemeData implements IFileIconTheme { private constructor() { } - public ensureLoaded(fileService: IFileService): Promise { + public ensureLoaded(fileService: IFileService): Promise { return !this.isLoaded ? this.load(fileService) : Promise.resolve(this.styleSheetContent); } - public reload(fileService: IFileService): Promise { + public reload(fileService: IFileService): Promise { return this.load(fileService); } - private load(fileService: IFileService): Promise { + private load(fileService: IFileService): Promise { if (!this.location) { return Promise.resolve(this.styleSheetContent); } From 9175a0612c3c183c06988e197d4639d4b5b0742d Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 11:57:06 +0100 Subject: [PATCH 1057/1066] no snippets when intellisense is triggered via space-character, #67714 --- .../electron-browser/snippetCompletionProvider.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts index f2abc6ac957..8e8456fb85a 100644 --- a/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/parts/snippets/electron-browser/snippetCompletionProvider.ts @@ -8,7 +8,7 @@ import { compare } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -import { CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, LanguageId, CompletionItemInsertTextRule } from 'vs/editor/common/modes'; +import { CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, LanguageId, CompletionItemInsertTextRule, CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes'; import { IModeService } from 'vs/editor/common/services/modeService'; import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser'; import { localize } from 'vs/nls'; @@ -73,12 +73,17 @@ export class SnippetCompletionProvider implements CompletionItemProvider { // } - provideCompletionItems(model: ITextModel, position: Position): Promise | undefined { + provideCompletionItems(model: ITextModel, position: Position, context: CompletionContext): Promise | undefined { if (position.column >= SnippetCompletionProvider._maxPrefix) { return undefined; } + if (context.triggerKind === CompletionTriggerKind.TriggerCharacter && context.triggerCharacter === ' ') { + // no snippets when suggestions have been triggered by space + return undefined; + } + const languageId = this._getLanguageIdAtPosition(model, position); return this._snippets.getSnippets(languageId).then(snippets => { From 2dcae22d7eae27290c10b96d24c19f2f9e3ddf07 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 6 Feb 2019 12:00:33 +0100 Subject: [PATCH 1058/1066] fix compilos --- .../electron-browser/snippetsService.test.ts | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts index 873bd9fd020..2986d7a5e2e 100644 --- a/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts +++ b/src/vs/workbench/parts/snippets/test/electron-browser/snippetsService.test.ts @@ -12,6 +12,7 @@ import { TextModel } from 'vs/editor/common/model/textModel'; import { ISnippetsService } from 'vs/workbench/parts/snippets/electron-browser/snippets.contribution'; import { Snippet, SnippetSource } from 'vs/workbench/parts/snippets/electron-browser/snippetsFile'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; +import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes'; class SimpleSnippetService implements ISnippetsService { _serviceBrand: any; @@ -39,6 +40,7 @@ suite('SnippetsService', function () { let modeService: ModeServiceImpl; let snippetService: ISnippetsService; + let context: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke }; setup(function () { modeService = new ModeServiceImpl(); @@ -67,7 +69,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); const model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1))!.then(result => { + return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); }); @@ -78,7 +80,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); const model = TextModel.createFromString('bar', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 4))!.then(result => { + return provider.provideCompletionItems(model, new Position(1, 4), context)!.then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].label, 'bar'); @@ -110,7 +112,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); const model = TextModel.createFromString('bar-bar', undefined, modeService.getLanguageIdentifier('fooLang')); - await provider.provideCompletionItems(model, new Position(1, 3))!.then(result => { + await provider.provideCompletionItems(model, new Position(1, 3), context)!.then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); assert.equal(result.suggestions[0].label, 'bar'); @@ -121,7 +123,7 @@ suite('SnippetsService', function () { assert.equal(result.suggestions[1].range.startColumn, 1); }); - await provider.provideCompletionItems(model, new Position(1, 5))!.then(result => { + await provider.provideCompletionItems(model, new Position(1, 5), context)!.then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].label, 'bar-bar'); @@ -129,7 +131,7 @@ suite('SnippetsService', function () { assert.equal(result.suggestions[0].range.startColumn, 1); }); - await provider.provideCompletionItems(model, new Position(1, 6))!.then(result => { + await provider.provideCompletionItems(model, new Position(1, 6), context)!.then(result => { assert.equal(result.incomplete, undefined); assert.equal(result.suggestions.length, 2); assert.equal(result.suggestions[0].label, 'bar'); @@ -155,19 +157,19 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('\t { + return provider.provideCompletionItems(model, new Position(1, 7), context)!.then(result => { assert.equal(result.suggestions.length, 1); model.dispose(); model = TextModel.createFromString('\t { assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].range.startColumn, 2); model.dispose(); model = TextModel.createFromString('a { assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].range.startColumn, 2); @@ -190,9 +192,9 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('\n\t\n>/head>', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1))!.then(result => { + return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.suggestions.length, 1); - return provider.provideCompletionItems(model, new Position(2, 2))!; + return provider.provideCompletionItems(model, new Position(2, 2), context)!; }).then(result => { assert.equal(result.suggestions.length, 1); }); @@ -220,7 +222,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('', undefined, modeService.getLanguageIdentifier('fooLang')); - return provider.provideCompletionItems(model, new Position(1, 1))!.then(result => { + return provider.provideCompletionItems(model, new Position(1, 1), context)!.then(result => { assert.equal(result.suggestions.length, 2); let [first, second] = result.suggestions; assert.equal(first.label, 'first'); @@ -242,13 +244,13 @@ suite('SnippetsService', function () { let model = TextModel.createFromString('p-', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 2))!; + let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 1); - result = await provider.provideCompletionItems(model, new Position(1, 3))!; + result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); - result = await provider.provideCompletionItems(model, new Position(1, 3))!; + result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); }); @@ -266,7 +268,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 158))!; + let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!; assert.equal(result.suggestions.length, 1); }); @@ -285,7 +287,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString(':', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 2))!; + let result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 0); }); @@ -304,7 +306,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('template', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 9))!; + let result = await provider.provideCompletionItems(model, new Position(1, 9), context)!; assert.equal(result.suggestions.length, 1); assert.equal(result.suggestions[0].label, 'mytemplate'); @@ -324,7 +326,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea Thisisaverylonglinegoingwithmore100bcharactersandthismakesintellisensebecomea b text_after_b', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 158))!; + let result = await provider.provideCompletionItems(model, new Position(1, 158), context)!; assert.equal(result.suggestions.length, 1); }); @@ -346,7 +348,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('.🐷-a-b', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 8))!; + let result = await provider.provideCompletionItems(model, new Position(1, 8), context)!; assert.equal(result.suggestions.length, 1); @@ -367,7 +369,7 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString('a ', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 3))!; + let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); }); @@ -394,14 +396,14 @@ suite('SnippetsService', function () { const provider = new SnippetCompletionProvider(modeService, snippetService); let model = TextModel.createFromString(' <', undefined, modeService.getLanguageIdentifier('fooLang')); - let result = await provider.provideCompletionItems(model, new Position(1, 3))!; + let result = await provider.provideCompletionItems(model, new Position(1, 3), context)!; assert.equal(result.suggestions.length, 1); let [first] = result.suggestions; assert.equal(first.range.startColumn, 2); model = TextModel.createFromString('1', undefined, modeService.getLanguageIdentifier('fooLang')); - result = await provider.provideCompletionItems(model, new Position(1, 2))!; + result = await provider.provideCompletionItems(model, new Position(1, 2), context)!; assert.equal(result.suggestions.length, 1); [first] = result.suggestions; From 9f479b17dee7eb4ec522ffbf6196c144c349ab71 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 6 Feb 2019 12:33:36 +0100 Subject: [PATCH 1059/1066] debt - extract a reusable NullExtensionService --- src/vs/workbench/electron-browser/shell.ts | 1 - .../test/common/commandService.test.ts | 61 ++----------------- .../services/extensions/common/extensions.ts | 24 ++++++++ .../workbench/test/workbenchTestServices.ts | 27 +------- 4 files changed, 32 insertions(+), 81 deletions(-) diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index ddd6d5862c7..593c56b37ca 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -369,7 +369,6 @@ export class Shell extends Disposable { serviceCollection.set(IMarkerService, new SyncDescriptor(MarkerService, undefined, true)); - serviceCollection.set(IModeService, new SyncDescriptor(WorkbenchModeServiceImpl)); serviceCollection.set(ITextResourceConfigurationService, new SyncDescriptor(TextResourceConfigurationService)); diff --git a/src/vs/workbench/services/commands/test/common/commandService.test.ts b/src/vs/workbench/services/commands/test/common/commandService.test.ts index b686e562f1b..4eef877efd0 100644 --- a/src/vs/workbench/services/commands/test/common/commandService.test.ts +++ b/src/vs/workbench/services/commands/test/common/commandService.test.ts @@ -6,59 +6,10 @@ import * as assert from 'assert'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { CommandService } from 'vs/workbench/services/commands/common/commandService'; -import { IExtensionService, ExtensionPointContribution, IExtensionDescription, ProfileSession } from 'vs/workbench/services/extensions/common/extensions'; +import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { Event, Emitter } from 'vs/base/common/event'; import { NullLogService } from 'vs/platform/log/common/log'; -class SimpleExtensionService implements IExtensionService { - _serviceBrand: any; - private _onDidRegisterExtensions = new Emitter(); - get onDidRegisterExtensions(): Event { - return this._onDidRegisterExtensions.event; - } - onDidChangeExtensionsStatus = null!; - onDidChangeExtensions = null!; - onWillActivateByEvent = null!; - onDidChangeResponsiveChange = null!; - activateByEvent(activationEvent: string): Promise { - return this.whenInstalledExtensionsRegistered().then(() => { }); - } - whenInstalledExtensionsRegistered(): Promise { - return Promise.resolve(true); - } - readExtensionPointContributions(extPoint: IExtensionPoint): Promise[]> { - return Promise.resolve([]); - } - getExtensionsStatus() { - return undefined!; - } - getExtensions(): Promise { - return Promise.resolve([]); - } - getExtension() { - return Promise.resolve(undefined); - } - canProfileExtensionHost() { - return false; - } - startExtensionHostProfile(): Promise { - throw new Error('Not implemented'); - } - getInspectPort(): number { - return 0; - } - restartExtensionHost(): void { - } - startExtensionHost(): void { - } - stopExtensionHost(): void { - } - canAddExtension(): boolean { return false; } - canRemoveExtension(): boolean { return false; } -} - suite('CommandService', function () { let commandRegistration: IDisposable; @@ -75,7 +26,7 @@ suite('CommandService', function () { let lastEvent: string; - let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { activateByEvent(activationEvent: string): Promise { lastEvent = activationEvent; return super.activateByEvent(activationEvent); @@ -94,7 +45,7 @@ suite('CommandService', function () { test('fwd activation error', async function () { - const extensionService = new class extends SimpleExtensionService { + const extensionService = new class extends NullExtensionService { activateByEvent(activationEvent: string): Promise { return Promise.reject(new Error('bad_activate')); } @@ -114,7 +65,7 @@ suite('CommandService', function () { let callCounter = 0; let reg = CommandsRegistry.registerCommand('bar', () => callCounter += 1); - let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { whenInstalledExtensionsRegistered() { return new Promise(_resolve => { /*ignore*/ }); } @@ -131,7 +82,7 @@ suite('CommandService', function () { let resolveFunc: Function; const whenInstalledExtensionsRegistered = new Promise(_resolve => { resolveFunc = _resolve; }); - let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { whenInstalledExtensionsRegistered() { return whenInstalledExtensionsRegistered; } @@ -154,7 +105,7 @@ suite('CommandService', function () { let callCounter = 0; let dispoables: IDisposable[] = []; let events: string[] = []; - let service = new CommandService(new InstantiationService(), new class extends SimpleExtensionService { + let service = new CommandService(new InstantiationService(), new class extends NullExtensionService { activateByEvent(event: string): Promise { events.push(event); diff --git a/src/vs/workbench/services/extensions/common/extensions.ts b/src/vs/workbench/services/extensions/common/extensions.ts index 4a7f1019f4a..fabecb6b379 100644 --- a/src/vs/workbench/services/extensions/common/extensions.ts +++ b/src/vs/workbench/services/extensions/common/extensions.ts @@ -259,3 +259,27 @@ export function toExtension(extensionDescription: IExtensionDescription): IExten location: extensionDescription.extensionLocation, }; } + + +export class NullExtensionService implements IExtensionService { + _serviceBrand: any; + onDidRegisterExtensions: Event = Event.None; + onDidChangeExtensionsStatus: Event = Event.None; + onDidChangeExtensions: Event = Event.None; + onWillActivateByEvent: Event = Event.None; + onDidChangeResponsiveChange: Event = Event.None; + activateByEvent(_activationEvent: string): Promise { return Promise.resolve(undefined); } + whenInstalledExtensionsRegistered(): Promise { return Promise.resolve(true); } + getExtensions(): Promise { return Promise.resolve([]); } + getExtension() { return Promise.resolve(undefined); } + readExtensionPointContributions(_extPoint: IExtensionPoint): Promise[]> { return Promise.resolve(Object.create(null)); } + getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } + canProfileExtensionHost(): boolean { return false; } + getInspectPort(): number { return 0; } + startExtensionHostProfile(): Promise { return Promise.resolve(Object.create(null)); } + restartExtensionHost(): void { } + startExtensionHost(): void { } + stopExtensionHost(): void { } + canAddExtension(): boolean { return false; } + canRemoveExtension(): boolean { return false; } +} \ No newline at end of file diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index a28b4952f26..9e56b9e2685 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -57,8 +57,7 @@ import { Range } from 'vs/editor/common/core/range'; import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; -import { IExtensionService, ProfileSession, IExtensionsStatus, ExtensionPointContribution, IExtensionDescription, IWillActivateEvent, IResponsiveStateChangeEvent } from '../services/extensions/common/extensions'; -import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/browser/decorations'; import { IDisposable, toDisposable, Disposable } from 'vs/base/common/lifecycle'; @@ -78,7 +77,6 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { @@ -310,28 +308,7 @@ export class TestDecorationsService implements IDecorationsService { getDecoration(_uri: URI, _includeChildren: boolean, _overwrite?: IDecorationData): IDecoration { return undefined; } } -export class TestExtensionService implements IExtensionService { - _serviceBrand: any; - onDidRegisterExtensions: Event = Event.None; - onDidChangeExtensionsStatus: Event = Event.None; - onDidChangeExtensions: Event = Event.None; - onWillActivateByEvent: Event = Event.None; - onDidChangeResponsiveChange: Event = Event.None; - activateByEvent(_activationEvent: string): Promise { return Promise.resolve(undefined); } - whenInstalledExtensionsRegistered(): Promise { return Promise.resolve(true); } - getExtensions(): Promise { return Promise.resolve([]); } - getExtension() { return Promise.resolve(undefined); } - readExtensionPointContributions(_extPoint: IExtensionPoint): Promise[]> { return Promise.resolve(Object.create(null)); } - getExtensionsStatus(): { [id: string]: IExtensionsStatus; } { return Object.create(null); } - canProfileExtensionHost(): boolean { return false; } - getInspectPort(): number { return 0; } - startExtensionHostProfile(): Promise { return Promise.resolve(Object.create(null)); } - restartExtensionHost(): void { } - startExtensionHost(): void { } - stopExtensionHost(): void { } - canAddExtension(): boolean { return false; } - canRemoveExtension(): boolean { return false; } -} +export class TestExtensionService extends NullExtensionService { } export class TestMenuService implements IMenuService { From 13e506e5d51b0946597031048639a35f4f1fd32a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 6 Feb 2019 12:39:41 +0100 Subject: [PATCH 1060/1066] debt - group extension service related things in one place --- src/tsconfig.strictNullChecks.json | 2 +- .../{ => services/extensions}/common/extensionHostProtocol.ts | 0 .../services/extensions/electron-browser/extensionHost.ts | 2 +- .../workbench/services/extensions/node/extensionHostProcess.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/workbench/{ => services/extensions}/common/extensionHostProtocol.ts (100%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index 9c4563e91f7..62d2c63d749 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -472,7 +472,6 @@ "./vs/workbench/common/editor/dataUriEditorInput.ts", "./vs/workbench/common/editor/diffEditorModel.ts", "./vs/workbench/common/editor/editorGroup.ts", - "./vs/workbench/common/extensionHostProtocol.ts", "./vs/workbench/common/memento.ts", "./vs/workbench/common/notifications.ts", "./vs/workbench/common/panel.ts", @@ -696,6 +695,7 @@ "./vs/workbench/services/editor/common/editorService.ts", "./vs/workbench/services/extensions/common/extensions.ts", "./vs/workbench/services/extensions/common/extensionsRegistry.ts", + "./vs/workbench/services/extensions/common/extensionHostProtocol.ts", "./vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts", "./vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts", "./vs/workbench/services/extensions/electron-browser/inactiveExtensionUrlHandler.ts", diff --git a/src/vs/workbench/common/extensionHostProtocol.ts b/src/vs/workbench/services/extensions/common/extensionHostProtocol.ts similarity index 100% rename from src/vs/workbench/common/extensionHostProtocol.ts rename to src/vs/workbench/services/extensions/common/extensionHostProtocol.ts diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 1749a27e1a6..a898b22b164 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -33,7 +33,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ICrashReporterService } from 'vs/workbench/services/crashReporter/electron-browser/crashReporterService'; import { IExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts index 40f22930a7e..fb3190e3c74 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcess.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcess.ts @@ -11,7 +11,7 @@ import { IMessagePassingProtocol } from 'vs/base/parts/ipc/node/ipc'; import { Protocol } from 'vs/base/parts/ipc/node/ipc.net'; import product from 'vs/platform/node/product'; import { IInitData } from 'vs/workbench/api/node/extHost.protocol'; -import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/common/extensionHostProtocol'; +import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { exit, ExtensionHostMain } from 'vs/workbench/services/extensions/node/extensionHostMain'; // With Electron 2.x and node.js 8.x the "natives" module From b0b6dc6c2eea0f587cb060af074b91af89ad7e11 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 6 Feb 2019 15:16:49 +0100 Subject: [PATCH 1061/1066] Update grammars to pick up make changes and fix hashes. --- build/npm/update-grammar.js | 39 +++++++++++-------- extensions/cpp/cgmanifest.json | 4 +- extensions/git/cgmanifest.json | 2 +- extensions/html/cgmanifest.json | 2 +- extensions/make/cgmanifest.json | 2 +- extensions/make/syntaxes/make.tmLanguage.json | 9 ++++- extensions/php/cgmanifest.json | 4 +- extensions/scss/cgmanifest.json | 2 +- extensions/sql/cgmanifest.json | 2 +- extensions/sql/syntaxes/sql.tmLanguage.json | 4 +- 10 files changed, 41 insertions(+), 29 deletions(-) diff --git a/build/npm/update-grammar.js b/build/npm/update-grammar.js index b4f94044419..522b2941218 100644 --- a/build/npm/update-grammar.js +++ b/build/npm/update-grammar.js @@ -12,6 +12,8 @@ var cson = require('cson-parser'); var https = require('https'); var url = require('url'); +let commitDate = '0000-00-00'; + /** * @param {string} urlString */ @@ -120,30 +122,35 @@ exports.update = function (repoId, repoPath, dest, modifyGrammar, version = 'mas try { fs.writeFileSync(dest, JSON.stringify(result, null, '\t').replace(/\n/g, '\r\n')); - // Add commit sha to cgmanifest let cgmanifestRead = JSON.parse(fs.readFileSync('./cgmanifest.json').toString()); let promises = new Array(); - let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/package.json`; - for (let i = 0; i < cgmanifestRead.registrations.length; i++) { - if (cgmanifestRead.registrations[i].component.git.repositoryUrl.substr(cgmanifestRead.registrations[i].component.git.repositoryUrl.length - repoId.length, repoId.length) === repoId) { - cgmanifestRead.registrations[i].component.git.commitHash = info.commitSha; - promises.push(download(packageJsonPath).then(function (packageJson) { - if (packageJson) { - try { - cgmanifestRead.registrations[i].version = JSON.parse(packageJson).version; - } catch (e) { - console.log('File does not exist at' + packageJsonPath); - } - } - })); - break; + const currentCommitDate = info.commitDate.substr(0, 10); + + // Add commit sha to cgmanifest. + if (currentCommitDate > commitDate) { + let packageJsonPath = 'https://raw.githubusercontent.com/' + repoId + `/${info.commitSha}/package.json`; + for (let i = 0; i < cgmanifestRead.registrations.length; i++) { + if (cgmanifestRead.registrations[i].component.git.repositoryUrl.substr(cgmanifestRead.registrations[i].component.git.repositoryUrl.length - repoId.length, repoId.length) === repoId) { + cgmanifestRead.registrations[i].component.git.commitHash = info.commitSha; + commitDate = currentCommitDate; + promises.push(download(packageJsonPath).then(function (packageJson) { + if (packageJson) { + try { + cgmanifestRead.registrations[i].version = JSON.parse(packageJson).version; + } catch (e) { + console.log('Cannot get version. File does not exist at ' + packageJsonPath); + } + } + })); + break; + } } } Promise.all(promises).then(function (allResult) { fs.writeFileSync('./cgmanifest.json', JSON.stringify(cgmanifestRead, null, '\t').replace(/\n/g, '\r\n')); }); if (info) { - console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + info.commitDate.substr(0, 10) + ')'); + console.log('Updated ' + path.basename(dest) + ' to ' + repoId + '@' + info.commitSha.substr(0, 7) + ' (' + currentCommitDate + ')'); } else { console.log('Updated ' + path.basename(dest)); } diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index 7bab26bfa11..e6666933f61 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "atom/language-c", "repositoryUrl": "https://github.com/atom/language-c", - "commitHash": "3a269f88b12e512fb9495dc006a1dabf325d3d7f" + "commitHash": "9c0c5f202741a5647025db8d5df5fefba47b036c" } }, "license": "MIT", - "version": "0.58.0", + "version": "0.58.1", "description": "The files syntaxes/c.json and syntaxes/c++.json were derived from the Atom package https://atom.io/packages/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/git/cgmanifest.json b/extensions/git/cgmanifest.json index 8b62f6fbb0c..d0bdb9ac443 100644 --- a/extensions/git/cgmanifest.json +++ b/extensions/git/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/git.tmbundle", "repositoryUrl": "https://github.com/textmate/git.tmbundle", - "commitHash": "93897a78c6e52bef13dadc0d4091d203c5facb40" + "commitHash": "3f6ad2138200db14b57a090ecb2d2e733275ca3e" } }, "licenseDetail": [ diff --git a/extensions/html/cgmanifest.json b/extensions/html/cgmanifest.json index fb7007ddb31..78125476f1e 100644 --- a/extensions/html/cgmanifest.json +++ b/extensions/html/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/html.tmbundle", "repositoryUrl": "https://github.com/textmate/html.tmbundle", - "commitHash": "390c8870273a2ae80244dae6db6ba064a802f407" + "commitHash": "0c3d5ee54de3a993f747f54186b73a4d2d3c44a2" } }, "licenseDetail": [ diff --git a/extensions/make/cgmanifest.json b/extensions/make/cgmanifest.json index e575c65097b..6f4432b66da 100644 --- a/extensions/make/cgmanifest.json +++ b/extensions/make/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "fadeevab/make.tmbundle", "repositoryUrl": "https://github.com/fadeevab/make.tmbundle", - "commitHash": "d01ce3404f9e2eed0fd4d07b5e24094c03599241" + "commitHash": "21e9108e9dce13b798667806bb105d852ac0a58c" } }, "licenseDetail": [ diff --git a/extensions/make/syntaxes/make.tmLanguage.json b/extensions/make/syntaxes/make.tmLanguage.json index 79a3082a0bb..878e78767e8 100644 --- a/extensions/make/syntaxes/make.tmLanguage.json +++ b/extensions/make/syntaxes/make.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/fadeevab/make.tmbundle/commit/d01ce3404f9e2eed0fd4d07b5e24094c03599241", + "version": "https://github.com/fadeevab/make.tmbundle/commit/21e9108e9dce13b798667806bb105d852ac0a58c", "name": "Makefile", "scopeName": "source.makefile", "patterns": [ @@ -342,7 +342,12 @@ "begin": "(^[ ]*|\\G\\s*)([^\\s]+)\\s*((? Date: Wed, 6 Feb 2019 16:04:59 +0100 Subject: [PATCH 1062/1066] fixes #67938 --- src/vs/base/browser/ui/list/listWidget.ts | 4 ++-- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ec06bc2dcb5..0f4c0798f67 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -527,7 +527,7 @@ export class MouseController implements IDisposable { Gesture.addTarget(list.getHTMLElement()); } - private isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { + protected isSelectionSingleChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { if (this.multipleSelectionController) { return this.multipleSelectionController.isSelectionSingleChangeEvent(event); } @@ -535,7 +535,7 @@ export class MouseController implements IDisposable { return platform.isMacintosh ? event.browserEvent.metaKey : event.browserEvent.ctrlKey; } - private isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { + protected isSelectionRangeChangeEvent(event: IListMouseEvent | IListTouchEvent): boolean { if (this.multipleSelectionController) { return this.multipleSelectionController.isSelectionRangeChangeEvent(event); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index d3eb4876738..4a71df669e8 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -796,7 +796,7 @@ class TreeNodeListMouseController extends MouseController< return super.onPointer(e); } - if (this.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.multipleSelectionController.isSelectionSingleChangeEvent(e)) { + if (this.isSelectionRangeChangeEvent(e) || this.isSelectionSingleChangeEvent(e)) { return super.onPointer(e); } From 4172bc6a898536f520f6da438b7b9d528f749233 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 16:36:45 +0100 Subject: [PATCH 1063/1066] fixes #67936 --- src/vs/base/browser/ui/tree/abstractTree.ts | 55 +++++++++++---------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 4a71df669e8..ee09be38112 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -531,7 +531,10 @@ class TypeFilterController implements IDisposable { this._pattern = pattern; this.filter.pattern = pattern; this.tree.refilter(); - this.tree.focusNext(0, true); + + if (pattern) { + this.tree.focusNext(0, true, undefined, node => !FuzzyScore.isDefault(node.filterData as any as FuzzyScore)); + } const focus = this.tree.getFocus(); @@ -648,6 +651,18 @@ class TypeFilterController implements IDisposable { this.labelDomNode.textContent = this.pattern.length > 16 ? '…' + this.pattern.substr(this.pattern.length - 16) : this.pattern; } + shouldAllowFocus(node: ITreeNode): boolean { + if (!this.enabled || !this.pattern || this.filterOnType) { + return true; + } + + if (this.filter.totalCount > 0 && this.filter.matchCount <= 1) { + return true; + } + + return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); + } + dispose() { this.disable(); this.disposables = dispose(this.disposables); @@ -898,12 +913,12 @@ export abstract class AbstractTree implements IDisposable private view: TreeNodeList; private renderers: TreeRenderer[]; - private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected model: ITreeModel; private focus: Trait; private selection: Trait; private eventBufferer = new EventBufferer(); private typeFilterController?: TypeFilterController; + private focusNavigationFilter: ((node: ITreeNode) => boolean) | undefined; protected disposables: IDisposable[] = []; get onDidScroll(): Event { return this.view.onDidScroll; } @@ -981,17 +996,7 @@ export abstract class AbstractTree implements IDisposable if (_options.keyboardNavigationLabelProvider) { this.typeFilterController = new TypeFilterController(this, this.model, this.view, filter!, _options.keyboardNavigationLabelProvider); - this.focusNavigationFilter = node => { - if (!this.typeFilterController!.enabled || !this.typeFilterController!.pattern || this.typeFilterController!.filterOnType) { - return true; - } - - if (filter!.totalCount > 0 && filter!.matchCount <= 1) { - return true; - } - - return !FuzzyScore.isDefault(node.filterData as any as FuzzyScore); - }; + this.focusNavigationFilter = node => this.typeFilterController!.shouldAllowFocus(node); this.disposables.push(this.typeFilterController!); } } @@ -1147,28 +1152,28 @@ export abstract class AbstractTree implements IDisposable this.view.setFocus(indexes, browserEvent, true); } - focusNext(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusNext(n, loop, browserEvent, this.focusNavigationFilter); + focusNext(n = 1, loop = false, browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusNext(n, loop, browserEvent, filter); } - focusPrevious(n = 1, loop = false, browserEvent?: UIEvent): void { - this.view.focusPrevious(n, loop, browserEvent, this.focusNavigationFilter); + focusPrevious(n = 1, loop = false, browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusPrevious(n, loop, browserEvent, filter); } - focusNextPage(browserEvent?: UIEvent): void { - this.view.focusNextPage(browserEvent, this.focusNavigationFilter); + focusNextPage(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusNextPage(browserEvent, filter); } - focusPreviousPage(browserEvent?: UIEvent): void { - this.view.focusPreviousPage(browserEvent, this.focusNavigationFilter); + focusPreviousPage(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusPreviousPage(browserEvent, filter); } - focusLast(browserEvent?: UIEvent): void { - this.view.focusLast(browserEvent, this.focusNavigationFilter); + focusLast(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusLast(browserEvent, filter); } - focusFirst(browserEvent?: UIEvent): void { - this.view.focusFirst(browserEvent, this.focusNavigationFilter); + focusFirst(browserEvent?: UIEvent, filter = this.focusNavigationFilter): void { + this.view.focusFirst(browserEvent, filter); } getFocus(): T[] { From 58a4013f82ff7f0ef3cdc9b1bba4066decb5a7da Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 6 Feb 2019 16:57:27 +0100 Subject: [PATCH 1064/1066] Empty string task pick inputs should resolve to empty string value Fixes #67927 --- .../workbench/parts/tasks/electron-browser/task.contribution.ts | 1 + .../parts/tasks/electron-browser/terminalTaskSystem.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) 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 07a32bc0a65..4e1cccf03be 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -1824,6 +1824,7 @@ class TaskService extends Disposable implements ITaskService { } else if (err instanceof Error) { let error = err; this.notificationService.error(error.message); + showOutput = false; } else if (Types.isString(err)) { this.notificationService.error(err); } else { diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 305d2c05c46..a0b4c778c8d 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -59,7 +59,7 @@ class VariableResolver { return value.replace(/\$\{(.*?)\}/g, (match: string, variable: string) => { // Strip out the ${} because the map contains them variables without those characters. let result = this._values.get(match.substring(2, match.length - 1)); - if (result) { + if ((result !== undefined) && (result !== null)) { return result; } if (this._service) { From f5dacc5bf3566015d0082b6d575a122298dd1ea4 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Wed, 6 Feb 2019 08:42:10 -0800 Subject: [PATCH 1065/1066] remove duplicate font declarations for menu (#67942) * remove font declarations from menu.css * remove decalarations from shell.css --- src/vs/base/browser/ui/menu/menu.css | 2 - .../electron-browser/media/shell.css | 45 +++++++------------ 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.css b/src/vs/base/browser/ui/menu/menu.css index 14ced9b853a..f1cdb6f3e45 100644 --- a/src/vs/base/browser/ui/menu/menu.css +++ b/src/vs/base/browser/ui/menu/menu.css @@ -117,7 +117,6 @@ /* Context Menu */ .context-view.monaco-menu-container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; -webkit-animation: fadeIn 0.083s linear; @@ -182,7 +181,6 @@ } .menubar .menubar-menu-items-holder.monaco-menu-container { - font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif; outline: 0; border: none; } diff --git a/src/vs/workbench/electron-browser/media/shell.css b/src/vs/workbench/electron-browser/media/shell.css index e3e3172eafe..1162363e9f8 100644 --- a/src/vs/workbench/electron-browser/media/shell.css +++ b/src/vs/workbench/electron-browser/media/shell.css @@ -16,38 +16,23 @@ /* Font Families (with CJK support) */ /* mac */ -.monaco-shell.mac, -.monaco-shell.mac .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } -.monaco-shell.mac:lang(zh-Hans), -.monaco-shell.mac:lang(zh-Hans) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } -.monaco-shell.mac:lang(zh-Hant), -.monaco-shell.mac:lang(zh-Hant) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } -.monaco-shell.mac:lang(ja), -.monaco-shell.mac:lang(ja) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } -.monaco-shell.mac:lang(ko), -.monaco-shell.mac:lang(ko) .monaco-menu-container .monaco-menu { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } +.monaco-shell.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } +.monaco-shell.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } +.monaco-shell.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } +.monaco-shell.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } +.monaco-shell.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } /* windows */ -.monaco-shell.windows, -.monaco-shell.windows .monaco-menu-container .monaco-menu { font-family: "Segoe WPC", "Segoe UI", sans-serif; } -.monaco-shell.windows:lang(zh-Hans), -.monaco-shell.windows:lang(zh-Hans) .monaco-menu-container .monaco-menu { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } -.monaco-shell.windows:lang(zh-Hant), -.monaco-shell.windows:lang(zh-Hant) .monaco-menu-container .monaco-menu { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } -.monaco-shell.windows:lang(ja), -.monaco-shell.windows:lang(ja) .monaco-menu-container .monaco-menu { font-family: "Segoe WPC", "Segoe UI", "Meiryo", sans-serif; } -.monaco-shell.windows:lang(ko), -.monaco-shell.windows:lang(ko) .monaco-menu-container .monaco-menu { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } +.monaco-shell.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; } +.monaco-shell.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } +.monaco-shell.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } +.monaco-shell.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Meiryo", sans-serif; } +.monaco-shell.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } /* linux */ -.monaco-shell.linux, -.monaco-shell.linux .monaco-menu-container .monaco-menu { font-family: "Ubuntu", "Droid Sans", sans-serif; } -.monaco-shell.linux:lang(zh-Hans), -.monaco-shell.linux:lang(zh-Hans) .monaco-menu-container .monaco-menu { font-family: "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } -.monaco-shell.linux:lang(zh-Hant), -.monaco-shell.linux:lang(zh-Hant) .monaco-menu-container .monaco-menu { font-family: "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } -.monaco-shell.linux:lang(ja), -.monaco-shell.linux:lang(ja) .monaco-menu-container .monaco-menu { font-family: "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } -.monaco-shell.linux:lang(ko), -.monaco-shell.linux:lang(ko) .monaco-menu-container .monaco-menu { font-family: "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } +.monaco-shell.linux { font-family: "Ubuntu", "Droid Sans", sans-serif; } +.monaco-shell.linux:lang(zh-Hans) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } +.monaco-shell.linux:lang(zh-Hant) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } +.monaco-shell.linux:lang(ja) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } +.monaco-shell.linux:lang(ko) { font-family: "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } From 6d35d18ea1f0e8b3f5048fb2226c346f76d07558 Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 6 Feb 2019 17:46:25 +0100 Subject: [PATCH 1066/1066] fixes #67912 --- src/vs/base/browser/ui/tree/dataTree.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 99fcac4edb5..72a192a8bc6 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -95,17 +95,19 @@ export class DataTree extends AbstractTree boolean): void { - this.model.setChildren((element === this.input ? null : element) as T, this.createIterator(element, isCollapsed)); + this.model.setChildren((element === this.input ? null : element) as T, this.iterate(element, isCollapsed).elements); } - private createIterator(element: TInput | T, isCollapsed?: (el: T) => boolean): Iterator> { - const children = Iterator.fromArray(this.dataSource.getChildren(element)); + private iterate(element: TInput | T, isCollapsed?: (el: T) => boolean): { elements: Iterator>, size: number } { + const children = this.dataSource.getChildren(element); + const elements = Iterator.map>(Iterator.fromArray(children), element => { + const { elements: children, size } = this.iterate(element, isCollapsed); + const collapsed = size === 0 ? undefined : (isCollapsed && isCollapsed(element)); - return Iterator.map>(children, element => ({ - element, - children: this.createIterator(element), - collapsed: isCollapsed && isCollapsed(element) - })); + return { element, children, collapsed }; + }); + + return { elements, size: children.length }; } protected createModel(view: ISpliceable>, options: IDataTreeOptions): ITreeModel {